Heap-allocated value with associated lifetime

Hi,

I am thinking about whether I can get rid of passing the parameter v, but allocating a value inside the function "counter" with a passing lifetime.

use std::boxed::Box;
use std::cell::Cell;
use std::ops::Fn;

fn main() {
    fn counter<'a>(
        v: &'a Cell<i64>,
    ) -> (Box<dyn Fn(i64) -> i64>, Box<dyn Fn(i64) -> i64>) {
        (
            Box::new({
                move |d| {
                    v.set(v.get() + d);
                    return v.get();
                }
            }),
            Box::new({
                move |d| {
                    v.set(v.get() - d);
                    return v.get();
                }
            }),
        )
    };

    let state = Cell::new(0);
    let (inc, dec) = counter(&state);

    let res = inc(2);
    println!("{}", res);
    let res = inc(2);
    println!("{}", res);
    // drop(state); // uncomment for borrow error
    let res = dec(2);
    println!("{}", res);
}

Here I find something useful, in the example belo, I can coerce the value's lifetime to that of the input argument

// Make a constant with `'static` lifetime.
static NUM: i32 = 18;

// Returns a reference to `NUM` where its `'static`
// lifetime is coerced to that of the input argument.
fn coerce_static<'a>(_: &'a i32) -> &'a i32 {
    &NUM
}

fn main() {
    {
        // Make a `string` literal and print it:
        let static_string = "I'm in read-only memory";
        println!("static_string: {}", static_string);

        // When `static_string` goes out of scope, the reference
        // can no longer be used, but the data remains in the binary.
    }

    {
        // Make an integer to use for `coerce_static`:
        let lifetime_num = 9;

        // Coerce `NUM` to lifetime of `lifetime_num`:
        let coerced_static = coerce_static(&lifetime_num);

        println!("coerced_static: {}", coerced_static);
    }

    println!("NUM: {} stays accessible!", NUM);
}

I am wondering whether I can replace the "static" with some heap-allocated thing, such as Box?

You can't allocate something with a given lifetime. Lifetimes don't do anything, and can't affect semantics of the code. They're only checks, not instructions.

Newly-created objects (like Cell<i32> or i32 or Box<str>) don't have any lifetimes, and lifetimes don't apply to them at all (confusingly, we say they (don't) live long enough, but that isn't necessarily described by a lifetime annotation in Rust). Lifetimes only apply to references, and become relevant only when an object is borrowed.

'static is a special case that means something can be borrowed until the end of the program. Leaked memory satisfies such requirement, so Box::leak returns 'static references. Of course it leaks, and trying to ever free such memory would be unsafe, so it's not a good idea to use it if you don't really mean to leak memory.

If you don't want to borrow any argument, and want to make a self-contained type instead, then you need the type to own the counter. In your case you have two closures that need to share the ownership. Shared-ownership types are Arc and Rc. So you need Rc<Cell<i64>> or Arc<AtomicI64> for the counter.

Hi, I think I have finished it. Is that what you mean?

use std::boxed::Box;
use std::cell::Cell;
use std::ops::Fn;
use std::rc::Rc;

fn main() {
    fn counter<'a>() -> (Box<dyn Fn(i64) -> i64>, Box<dyn Fn(i64) -> i64>) {
        // let mut v = Arc::new(AtomicI64::new(0));
        let v: Rc<Cell<i64>> = Rc::new(Cell::new(0));
        let b1 = Rc::clone(&v);
        let b2 = Rc::clone(&v);
        (
            Box::new({
                move |d| {
                    // let b = Rc::clone(&v);
                    // let tempv = b.borrow().get();
                    // b.replace(5);
                    b1.set(b1.get() + d);
                    return b1.get();
                    // return b.borrow().get();
                    // return b.into_inner();
                }
            }),
            Box::new({
                move |d| {
                    // let b = Rc::clone(&v);
                    // let tempv = b.borrow().get();
                    // b.replace(-2);
                    b2.set(b2.get() - d);
                    return b2.get();
                    // return b.borrow().get();
                    // return b.into_inner();
                }
            }),
        )
    };

    // let state = Cell::new(0);
    let (inc, dec) = counter();

    let res = inc(2);
    println!("{}", res);
    let res = inc(2);
    println!("{}", res);
    // drop(state); // uncomment for borrow error
    let res = dec(2);
    println!("{}", res);
}
1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.