I'm trying to pass a constructor function as an argument to another function. The function creates a struct with an associated lifetime. I need to create a struct from this pointer after I have created some other objects that this struct can then reference. The example below seems to work:
struct Bar<'a> {
number: Option<&'a usize>,
}
impl<'a> Bar<'a> {
pub fn new() -> Bar<'a> {
Bar { number: None }
}
}
fn foo<'a, F>(func: &F)
where
F: Fn() -> Bar<'a>,
{
let number = 42;
let mut bar = (func)();
bar.number = Some(&number);
}
fn main() {
foo(&Bar::new);
}
When I add a Cell
for interior mutability then it does not compile:
use std::cell::Cell;
struct Bar<'a> {
number: Cell<Option<&'a usize>>,
}
impl<'a> Bar<'a> {
pub fn new() -> Bar<'a> {
Bar {
number: Cell::new(None),
}
}
}
fn foo<'a, F>(func: &F)
where
F: Fn() -> Bar<'a>,
{
let number = 42;
let bar = (func)();
bar.number.set(Some(&number));
}
fn main() {
foo(&Bar::new);
}
Giving me the following error:
error[E0597]: `number` does not live long enough
--> src/main.rs:21:26
|
21 | bar.number.set(Some(&number));
| ^^^^^^ borrowed value does not live long enough
22 | }
| - borrowed value only lives until here
|
note: borrowed value must be valid for the lifetime 'a as defined on the function body at 15:1...
--> src/main.rs:15:1
|
15 | / fn foo<'a, F>(func: &F)
16 | | where
17 | | F: Fn() -> Bar<'a>,
18 | | {
... |
21 | | bar.number.set(Some(&number));
22 | | }
| |_^
Why did the first example work and not the second? Is there a way to specify a lifetime that exists for the scope let mut bar
until the end of the function, rather than 'a
which encompasses the entire function? Is this not possible without Non Lexical Lifetimes or Higher Kind Type Constructors etc?
The compiler is smarter than you give it credit for. It has prevented you from introducing memory unsafety:
fn foo<'a, F>(func: &F) where F: Fn() -> Bar<'a>, { let number = 42; let bar = (func)(); bar.number.set(Some(&number)); }
This code says that the caller of foo
can specify a lifetime for 'a
, but then the body of the method stores a reference into the value. That stored reference is not guaranteed to live that long. As an obvious example, the caller might require that 'a
== 'static
, but that would be impossible for the function to accomplish:
fn b() -> Bar<'static> {
Bar {
number: Cell::new(None),
}
}
fn main() {
foo(&b);
}
Note that this doesn't have anything to do with closures or functions:
use std::cell::Cell;
fn main() {
let number = Cell::new(None);
let x = 1;
number.set(Some(&x));
let y = 2;
number.set(Some(&y));
}
error[E0597]: `x` does not live long enough
--> src/main.rs:6:22
|
6 | number.set(Some(&x));
| ^ borrowed value does not live long enough
...
9 | }
| - `x` dropped here while still borrowed
|
= note: values in a scope are dropped in the opposite order they are created
Why did the first example work and not the second?
Because the compiler knows that Cell
(really UnsafeCell
) needs to account for the possibility that you will be storing a value in the created type.
From the Nomicon, emphasis mine:
UnsafeCell<T>
,Cell<T>
,RefCell<T>
,Mutex<T>
and all other interior mutability types are invariant overT
(as is*mut T
by metaphor)
Variance is a dense topic that I cannot explain succinctly.
@trentcl provides this example that shows that your original code may not be doing what you think it is.
Without the Cell
, the compiler knows that it's safe to automatically adjust the lifetime of the returned type to one that's a little bit shorter. If we force the type to be the longer 'a
, however, we get the same error:
fn foo<'a, F>(func: F)
where
F: Fn() -> Bar<'a>,
{
let number = 42;
let mut bar: Bar<'a> = func();
// ^^^^^^^
bar.number = Some(&number);
}
error[E0597]: `number` does not live long enough
--> src/main.rs:17:24
|
17 | bar.number = Some(&number);
| ^^^^^^ borrowed value does not live long enough
18 | }
| - borrowed value only lives until here
|
note: borrowed value must be valid for the lifetime 'a as defined on the function body at 11:1...
--> src/main.rs:11:1
|
11 | / fn foo<'a, F>(func: F)
12 | | where
13 | | F: Fn() -> Bar<'a>,
14 | | {
... |
17 | | bar.number = Some(&number);
18 | | }
| |_^
Is this not possible without [...]
Yes, but I'm not sure exactly what it would be. I believe it would need generic associated types (GAT) from RFC 1598.
My first thought was to try higher-ranked trait bounds (HRTB):
fn foo<F>(func: F)
where
F: for<'a> Fn() -> Bar<'a>,
{
let number = 42;
let bar = func();
bar.number.set(Some(&number));
}
This triggers E0582:
error[E0582]: binding for associated type `Output` references lifetime `'a`, which does not appear in the trait input types
--> src/main.rs:17:25
|
17 | F: for <'a> Fn() -> Bar<'a>,
| ^^^^^^^
To be honest, I cannot see the value in the code based on the example provided. If you are returning a Bar
by value, you can make it mutable, removing any need for interior mutability.
You can also change the closure to take the value as needed:
fn foo<F>(func: F)
where
F: for<'a> Fn(&'a i32) -> Bar<'a>,
{
let number = 42;
let bar = func(&number);
}
See also:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With