Why do lifetimes differ when a function is called in a closure vs. being called directly in Rust?
up vote
11
down vote
favorite
In the following code example:
fn default_values() -> &'static [u32] {
static VALUES: [u32; 3] = [1, 2, 3];
&VALUES
}
fn main() {
let values: [u32; 3] = [4, 5, 6];
let optional_values: Option<&[u32]> = Some(&values);
// this compiles and runs fine
let _v = optional_values.unwrap_or_else(|| default_values());
// this fails to compile
let _v = optional_values.unwrap_or_else(default_values);
}
the last statement fails to compile with:
error[E0597]: `values` does not live long enough
--> src/main.rs:8:49
|
8 | let optional_values: Option<&[u32]> = Some(&values);
| ^^^^^^ borrowed value does not live long enough
...
12 | }
| - borrowed value only lives until here
|
= note: borrowed value must be valid for the static lifetime...
I'm wondering:
- what's happening that causes the difference in behaviour between the last two statements
- whether the first
unwrap_or_else(|| default_values())is the correct way of handling this, or whether there's a better pattern
rust
add a comment |
up vote
11
down vote
favorite
In the following code example:
fn default_values() -> &'static [u32] {
static VALUES: [u32; 3] = [1, 2, 3];
&VALUES
}
fn main() {
let values: [u32; 3] = [4, 5, 6];
let optional_values: Option<&[u32]> = Some(&values);
// this compiles and runs fine
let _v = optional_values.unwrap_or_else(|| default_values());
// this fails to compile
let _v = optional_values.unwrap_or_else(default_values);
}
the last statement fails to compile with:
error[E0597]: `values` does not live long enough
--> src/main.rs:8:49
|
8 | let optional_values: Option<&[u32]> = Some(&values);
| ^^^^^^ borrowed value does not live long enough
...
12 | }
| - borrowed value only lives until here
|
= note: borrowed value must be valid for the static lifetime...
I'm wondering:
- what's happening that causes the difference in behaviour between the last two statements
- whether the first
unwrap_or_else(|| default_values())is the correct way of handling this, or whether there's a better pattern
rust
2
I don't understand why but I changed it todefault_values as fn() -> &'static _then it'll work.
– DanSnow
Nov 6 at 11:48
2
Nightly +feature(nll)gives an additional information: 'borrowed value does not live long enough, cast requires thatvaluesis borrowed for'static'
– hellow
Nov 6 at 12:02
Look like the compile infer_vbased on the return type of the function instead of infer_vbased on the type ofoptional_value, and actually onlyas fn() -> _is enough. That simply a case of "you need to help the compiler".
– Stargateur
Nov 6 at 12:50
add a comment |
up vote
11
down vote
favorite
up vote
11
down vote
favorite
In the following code example:
fn default_values() -> &'static [u32] {
static VALUES: [u32; 3] = [1, 2, 3];
&VALUES
}
fn main() {
let values: [u32; 3] = [4, 5, 6];
let optional_values: Option<&[u32]> = Some(&values);
// this compiles and runs fine
let _v = optional_values.unwrap_or_else(|| default_values());
// this fails to compile
let _v = optional_values.unwrap_or_else(default_values);
}
the last statement fails to compile with:
error[E0597]: `values` does not live long enough
--> src/main.rs:8:49
|
8 | let optional_values: Option<&[u32]> = Some(&values);
| ^^^^^^ borrowed value does not live long enough
...
12 | }
| - borrowed value only lives until here
|
= note: borrowed value must be valid for the static lifetime...
I'm wondering:
- what's happening that causes the difference in behaviour between the last two statements
- whether the first
unwrap_or_else(|| default_values())is the correct way of handling this, or whether there's a better pattern
rust
In the following code example:
fn default_values() -> &'static [u32] {
static VALUES: [u32; 3] = [1, 2, 3];
&VALUES
}
fn main() {
let values: [u32; 3] = [4, 5, 6];
let optional_values: Option<&[u32]> = Some(&values);
// this compiles and runs fine
let _v = optional_values.unwrap_or_else(|| default_values());
// this fails to compile
let _v = optional_values.unwrap_or_else(default_values);
}
the last statement fails to compile with:
error[E0597]: `values` does not live long enough
--> src/main.rs:8:49
|
8 | let optional_values: Option<&[u32]> = Some(&values);
| ^^^^^^ borrowed value does not live long enough
...
12 | }
| - borrowed value only lives until here
|
= note: borrowed value must be valid for the static lifetime...
I'm wondering:
- what's happening that causes the difference in behaviour between the last two statements
- whether the first
unwrap_or_else(|| default_values())is the correct way of handling this, or whether there's a better pattern
rust
rust
asked Nov 6 at 11:36
Dave Challis
1,4651336
1,4651336
2
I don't understand why but I changed it todefault_values as fn() -> &'static _then it'll work.
– DanSnow
Nov 6 at 11:48
2
Nightly +feature(nll)gives an additional information: 'borrowed value does not live long enough, cast requires thatvaluesis borrowed for'static'
– hellow
Nov 6 at 12:02
Look like the compile infer_vbased on the return type of the function instead of infer_vbased on the type ofoptional_value, and actually onlyas fn() -> _is enough. That simply a case of "you need to help the compiler".
– Stargateur
Nov 6 at 12:50
add a comment |
2
I don't understand why but I changed it todefault_values as fn() -> &'static _then it'll work.
– DanSnow
Nov 6 at 11:48
2
Nightly +feature(nll)gives an additional information: 'borrowed value does not live long enough, cast requires thatvaluesis borrowed for'static'
– hellow
Nov 6 at 12:02
Look like the compile infer_vbased on the return type of the function instead of infer_vbased on the type ofoptional_value, and actually onlyas fn() -> _is enough. That simply a case of "you need to help the compiler".
– Stargateur
Nov 6 at 12:50
2
2
I don't understand why but I changed it to
default_values as fn() -> &'static _ then it'll work.– DanSnow
Nov 6 at 11:48
I don't understand why but I changed it to
default_values as fn() -> &'static _ then it'll work.– DanSnow
Nov 6 at 11:48
2
2
Nightly +
feature(nll) gives an additional information: 'borrowed value does not live long enough, cast requires that values is borrowed for 'static'– hellow
Nov 6 at 12:02
Nightly +
feature(nll) gives an additional information: 'borrowed value does not live long enough, cast requires that values is borrowed for 'static'– hellow
Nov 6 at 12:02
Look like the compile infer
_v based on the return type of the function instead of infer _v based on the type of optional_value, and actually only as fn() -> _ is enough. That simply a case of "you need to help the compiler".– Stargateur
Nov 6 at 12:50
Look like the compile infer
_v based on the return type of the function instead of infer _v based on the type of optional_value, and actually only as fn() -> _ is enough. That simply a case of "you need to help the compiler".– Stargateur
Nov 6 at 12:50
add a comment |
2 Answers
2
active
oldest
votes
up vote
5
down vote
accepted
This happens because default_values implements Fn() -> &'static [u32], but not for<'a> Fn() -> &'a [u32]. Traits are invariant, so you can't coerce "something that implements Fn() -> &'static [u32]" to "something that implements Fn() -> &'a [u32]" (for some 'a smaller than 'static), even though, logically speaking, default_values could satisfy both.
When it's called in a closure, default_values() returns a &'static [u32], but it can be coerced immediately to a &'a u32, making the closure itself able to implement Fn() -> &'a [u32] (where the &'a is determined by the compiler).
As for why adding as fn() -> &'static [u32] works, I assume the compiler can recognize that the function pointer type fn() -> &'static [u32] is capable of implementing Fn() -> &'a [u32] for any 'a. I'm not sure why it doesn't also do this for ordinary functions and closures; perhaps a future compiler version could be smart enough to allow the original code.
Another solution is to make the type of default_values one that can implement the Fn trait you need:
fn default_values<'a>() -> &'a [u32] {
static VALUES: [u32; 3] = [1, 2, 3];
&VALUES
}
Instead of saying "this is a function that returns a 'static reference", the signature here says "this is a function that can return a reference of any lifetime". We know that "a reference of any lifetime" has to be a 'static reference, but the compiler sees the signatures as different because this one has an additional degree of freedom. This change is sufficient to make your original example compile.
1
This still feels like a bug. Probably this is an unpolished edge of HRTB.
– Peter Hall
Nov 6 at 13:37
1
I agree, it does feel like a bug.
– trentcl
Nov 6 at 20:42
add a comment |
up vote
2
down vote
There is no difference between a closure and a direct function call: It is just a matter of type inference.
closure that compiles:
let _v = optional_values.unwrap_or_else(|| default_values());
let _v = optional_values.unwrap_or_else(|| -> & [u32] {default_values()});
closure that not compiles:
let _v = unwrap_or_else(optional_values, || -> &'static [u32] {default_values()});
function that compiles:
let _v = unwrap_or_else(optional_values, default_values as fn() -> &'static _);
function that not compiles:
let _v = unwrap_or_else(optional_values, default_values);
A litte bit of explanation
Consider this equivalent code:
fn default_values() -> &'static [u32] {
static VALUES: [u32; 3] = [1, 2, 3];
&VALUES
}
fn unwrap_or_else<T, F>(slf: Option<T>, f: F) -> T where
F: FnOnce() -> T, {
match slf {
Some(t) => t,
None => f()
}
}
the following snippet:
fn main() {
let values: [u32; 3] = [4, 5, 6];
let optional_values: Option<&[u32]> = Some(&values);
let _v = unwrap_or_else(optional_values, || -> &'static [u32] {default_values});
// the above throws the same error of:
//let _v = unwrap_or_else(optional_values, default_values);
}
fails:
error[E0597]: `values` does not live long enough
--> src/main.rs:18:48
|
18 | let optional_values: Option<&[u32]> = Some(&values);
| ^^^^^^^
| |
| borrowed value does not live long enough
| cast requires that `values` is borrowed for `'static`
...
27 | }
| - `values` dropped here while still borrowed
Look from the monomorphization side: assuming that
the compiler infers that T resolves to the concrete type &'static [u32],
and supposing that the produced code is something like:
fn unwrap_or_else_u32_sl_fn_u32_sl(slf: Option<&'static [u32]>,
f: fn() -> &'static [u32]) -> &'static [u32] {
...
}
then the above monomorphization explains the error:
slf value is optional_values: an Option<&'a [u32]> that does not live enough and clearly cannot be cast because it does not satisfies the 'static lifetime requirement.
If you write:
let _v = unwrap_or_else(optional_values, || default_values());
// the same, expliciting the return type:
let _v = unwrap_or_else(optional_values, || -> & [u32] {default_values()});
It compiles: now the lifetime of the return type is compatible with the optional_values lifetime.
Finally, I'm not able to explain why, but the evidence shows that the cast as fn() -> &'static _ helps the compiler to be sure that decoupling lifetimes bound to optional_values and default_values is safe.
add a comment |
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
5
down vote
accepted
This happens because default_values implements Fn() -> &'static [u32], but not for<'a> Fn() -> &'a [u32]. Traits are invariant, so you can't coerce "something that implements Fn() -> &'static [u32]" to "something that implements Fn() -> &'a [u32]" (for some 'a smaller than 'static), even though, logically speaking, default_values could satisfy both.
When it's called in a closure, default_values() returns a &'static [u32], but it can be coerced immediately to a &'a u32, making the closure itself able to implement Fn() -> &'a [u32] (where the &'a is determined by the compiler).
As for why adding as fn() -> &'static [u32] works, I assume the compiler can recognize that the function pointer type fn() -> &'static [u32] is capable of implementing Fn() -> &'a [u32] for any 'a. I'm not sure why it doesn't also do this for ordinary functions and closures; perhaps a future compiler version could be smart enough to allow the original code.
Another solution is to make the type of default_values one that can implement the Fn trait you need:
fn default_values<'a>() -> &'a [u32] {
static VALUES: [u32; 3] = [1, 2, 3];
&VALUES
}
Instead of saying "this is a function that returns a 'static reference", the signature here says "this is a function that can return a reference of any lifetime". We know that "a reference of any lifetime" has to be a 'static reference, but the compiler sees the signatures as different because this one has an additional degree of freedom. This change is sufficient to make your original example compile.
1
This still feels like a bug. Probably this is an unpolished edge of HRTB.
– Peter Hall
Nov 6 at 13:37
1
I agree, it does feel like a bug.
– trentcl
Nov 6 at 20:42
add a comment |
up vote
5
down vote
accepted
This happens because default_values implements Fn() -> &'static [u32], but not for<'a> Fn() -> &'a [u32]. Traits are invariant, so you can't coerce "something that implements Fn() -> &'static [u32]" to "something that implements Fn() -> &'a [u32]" (for some 'a smaller than 'static), even though, logically speaking, default_values could satisfy both.
When it's called in a closure, default_values() returns a &'static [u32], but it can be coerced immediately to a &'a u32, making the closure itself able to implement Fn() -> &'a [u32] (where the &'a is determined by the compiler).
As for why adding as fn() -> &'static [u32] works, I assume the compiler can recognize that the function pointer type fn() -> &'static [u32] is capable of implementing Fn() -> &'a [u32] for any 'a. I'm not sure why it doesn't also do this for ordinary functions and closures; perhaps a future compiler version could be smart enough to allow the original code.
Another solution is to make the type of default_values one that can implement the Fn trait you need:
fn default_values<'a>() -> &'a [u32] {
static VALUES: [u32; 3] = [1, 2, 3];
&VALUES
}
Instead of saying "this is a function that returns a 'static reference", the signature here says "this is a function that can return a reference of any lifetime". We know that "a reference of any lifetime" has to be a 'static reference, but the compiler sees the signatures as different because this one has an additional degree of freedom. This change is sufficient to make your original example compile.
1
This still feels like a bug. Probably this is an unpolished edge of HRTB.
– Peter Hall
Nov 6 at 13:37
1
I agree, it does feel like a bug.
– trentcl
Nov 6 at 20:42
add a comment |
up vote
5
down vote
accepted
up vote
5
down vote
accepted
This happens because default_values implements Fn() -> &'static [u32], but not for<'a> Fn() -> &'a [u32]. Traits are invariant, so you can't coerce "something that implements Fn() -> &'static [u32]" to "something that implements Fn() -> &'a [u32]" (for some 'a smaller than 'static), even though, logically speaking, default_values could satisfy both.
When it's called in a closure, default_values() returns a &'static [u32], but it can be coerced immediately to a &'a u32, making the closure itself able to implement Fn() -> &'a [u32] (where the &'a is determined by the compiler).
As for why adding as fn() -> &'static [u32] works, I assume the compiler can recognize that the function pointer type fn() -> &'static [u32] is capable of implementing Fn() -> &'a [u32] for any 'a. I'm not sure why it doesn't also do this for ordinary functions and closures; perhaps a future compiler version could be smart enough to allow the original code.
Another solution is to make the type of default_values one that can implement the Fn trait you need:
fn default_values<'a>() -> &'a [u32] {
static VALUES: [u32; 3] = [1, 2, 3];
&VALUES
}
Instead of saying "this is a function that returns a 'static reference", the signature here says "this is a function that can return a reference of any lifetime". We know that "a reference of any lifetime" has to be a 'static reference, but the compiler sees the signatures as different because this one has an additional degree of freedom. This change is sufficient to make your original example compile.
This happens because default_values implements Fn() -> &'static [u32], but not for<'a> Fn() -> &'a [u32]. Traits are invariant, so you can't coerce "something that implements Fn() -> &'static [u32]" to "something that implements Fn() -> &'a [u32]" (for some 'a smaller than 'static), even though, logically speaking, default_values could satisfy both.
When it's called in a closure, default_values() returns a &'static [u32], but it can be coerced immediately to a &'a u32, making the closure itself able to implement Fn() -> &'a [u32] (where the &'a is determined by the compiler).
As for why adding as fn() -> &'static [u32] works, I assume the compiler can recognize that the function pointer type fn() -> &'static [u32] is capable of implementing Fn() -> &'a [u32] for any 'a. I'm not sure why it doesn't also do this for ordinary functions and closures; perhaps a future compiler version could be smart enough to allow the original code.
Another solution is to make the type of default_values one that can implement the Fn trait you need:
fn default_values<'a>() -> &'a [u32] {
static VALUES: [u32; 3] = [1, 2, 3];
&VALUES
}
Instead of saying "this is a function that returns a 'static reference", the signature here says "this is a function that can return a reference of any lifetime". We know that "a reference of any lifetime" has to be a 'static reference, but the compiler sees the signatures as different because this one has an additional degree of freedom. This change is sufficient to make your original example compile.
answered Nov 6 at 12:50
trentcl
6,05731234
6,05731234
1
This still feels like a bug. Probably this is an unpolished edge of HRTB.
– Peter Hall
Nov 6 at 13:37
1
I agree, it does feel like a bug.
– trentcl
Nov 6 at 20:42
add a comment |
1
This still feels like a bug. Probably this is an unpolished edge of HRTB.
– Peter Hall
Nov 6 at 13:37
1
I agree, it does feel like a bug.
– trentcl
Nov 6 at 20:42
1
1
This still feels like a bug. Probably this is an unpolished edge of HRTB.
– Peter Hall
Nov 6 at 13:37
This still feels like a bug. Probably this is an unpolished edge of HRTB.
– Peter Hall
Nov 6 at 13:37
1
1
I agree, it does feel like a bug.
– trentcl
Nov 6 at 20:42
I agree, it does feel like a bug.
– trentcl
Nov 6 at 20:42
add a comment |
up vote
2
down vote
There is no difference between a closure and a direct function call: It is just a matter of type inference.
closure that compiles:
let _v = optional_values.unwrap_or_else(|| default_values());
let _v = optional_values.unwrap_or_else(|| -> & [u32] {default_values()});
closure that not compiles:
let _v = unwrap_or_else(optional_values, || -> &'static [u32] {default_values()});
function that compiles:
let _v = unwrap_or_else(optional_values, default_values as fn() -> &'static _);
function that not compiles:
let _v = unwrap_or_else(optional_values, default_values);
A litte bit of explanation
Consider this equivalent code:
fn default_values() -> &'static [u32] {
static VALUES: [u32; 3] = [1, 2, 3];
&VALUES
}
fn unwrap_or_else<T, F>(slf: Option<T>, f: F) -> T where
F: FnOnce() -> T, {
match slf {
Some(t) => t,
None => f()
}
}
the following snippet:
fn main() {
let values: [u32; 3] = [4, 5, 6];
let optional_values: Option<&[u32]> = Some(&values);
let _v = unwrap_or_else(optional_values, || -> &'static [u32] {default_values});
// the above throws the same error of:
//let _v = unwrap_or_else(optional_values, default_values);
}
fails:
error[E0597]: `values` does not live long enough
--> src/main.rs:18:48
|
18 | let optional_values: Option<&[u32]> = Some(&values);
| ^^^^^^^
| |
| borrowed value does not live long enough
| cast requires that `values` is borrowed for `'static`
...
27 | }
| - `values` dropped here while still borrowed
Look from the monomorphization side: assuming that
the compiler infers that T resolves to the concrete type &'static [u32],
and supposing that the produced code is something like:
fn unwrap_or_else_u32_sl_fn_u32_sl(slf: Option<&'static [u32]>,
f: fn() -> &'static [u32]) -> &'static [u32] {
...
}
then the above monomorphization explains the error:
slf value is optional_values: an Option<&'a [u32]> that does not live enough and clearly cannot be cast because it does not satisfies the 'static lifetime requirement.
If you write:
let _v = unwrap_or_else(optional_values, || default_values());
// the same, expliciting the return type:
let _v = unwrap_or_else(optional_values, || -> & [u32] {default_values()});
It compiles: now the lifetime of the return type is compatible with the optional_values lifetime.
Finally, I'm not able to explain why, but the evidence shows that the cast as fn() -> &'static _ helps the compiler to be sure that decoupling lifetimes bound to optional_values and default_values is safe.
add a comment |
up vote
2
down vote
There is no difference between a closure and a direct function call: It is just a matter of type inference.
closure that compiles:
let _v = optional_values.unwrap_or_else(|| default_values());
let _v = optional_values.unwrap_or_else(|| -> & [u32] {default_values()});
closure that not compiles:
let _v = unwrap_or_else(optional_values, || -> &'static [u32] {default_values()});
function that compiles:
let _v = unwrap_or_else(optional_values, default_values as fn() -> &'static _);
function that not compiles:
let _v = unwrap_or_else(optional_values, default_values);
A litte bit of explanation
Consider this equivalent code:
fn default_values() -> &'static [u32] {
static VALUES: [u32; 3] = [1, 2, 3];
&VALUES
}
fn unwrap_or_else<T, F>(slf: Option<T>, f: F) -> T where
F: FnOnce() -> T, {
match slf {
Some(t) => t,
None => f()
}
}
the following snippet:
fn main() {
let values: [u32; 3] = [4, 5, 6];
let optional_values: Option<&[u32]> = Some(&values);
let _v = unwrap_or_else(optional_values, || -> &'static [u32] {default_values});
// the above throws the same error of:
//let _v = unwrap_or_else(optional_values, default_values);
}
fails:
error[E0597]: `values` does not live long enough
--> src/main.rs:18:48
|
18 | let optional_values: Option<&[u32]> = Some(&values);
| ^^^^^^^
| |
| borrowed value does not live long enough
| cast requires that `values` is borrowed for `'static`
...
27 | }
| - `values` dropped here while still borrowed
Look from the monomorphization side: assuming that
the compiler infers that T resolves to the concrete type &'static [u32],
and supposing that the produced code is something like:
fn unwrap_or_else_u32_sl_fn_u32_sl(slf: Option<&'static [u32]>,
f: fn() -> &'static [u32]) -> &'static [u32] {
...
}
then the above monomorphization explains the error:
slf value is optional_values: an Option<&'a [u32]> that does not live enough and clearly cannot be cast because it does not satisfies the 'static lifetime requirement.
If you write:
let _v = unwrap_or_else(optional_values, || default_values());
// the same, expliciting the return type:
let _v = unwrap_or_else(optional_values, || -> & [u32] {default_values()});
It compiles: now the lifetime of the return type is compatible with the optional_values lifetime.
Finally, I'm not able to explain why, but the evidence shows that the cast as fn() -> &'static _ helps the compiler to be sure that decoupling lifetimes bound to optional_values and default_values is safe.
add a comment |
up vote
2
down vote
up vote
2
down vote
There is no difference between a closure and a direct function call: It is just a matter of type inference.
closure that compiles:
let _v = optional_values.unwrap_or_else(|| default_values());
let _v = optional_values.unwrap_or_else(|| -> & [u32] {default_values()});
closure that not compiles:
let _v = unwrap_or_else(optional_values, || -> &'static [u32] {default_values()});
function that compiles:
let _v = unwrap_or_else(optional_values, default_values as fn() -> &'static _);
function that not compiles:
let _v = unwrap_or_else(optional_values, default_values);
A litte bit of explanation
Consider this equivalent code:
fn default_values() -> &'static [u32] {
static VALUES: [u32; 3] = [1, 2, 3];
&VALUES
}
fn unwrap_or_else<T, F>(slf: Option<T>, f: F) -> T where
F: FnOnce() -> T, {
match slf {
Some(t) => t,
None => f()
}
}
the following snippet:
fn main() {
let values: [u32; 3] = [4, 5, 6];
let optional_values: Option<&[u32]> = Some(&values);
let _v = unwrap_or_else(optional_values, || -> &'static [u32] {default_values});
// the above throws the same error of:
//let _v = unwrap_or_else(optional_values, default_values);
}
fails:
error[E0597]: `values` does not live long enough
--> src/main.rs:18:48
|
18 | let optional_values: Option<&[u32]> = Some(&values);
| ^^^^^^^
| |
| borrowed value does not live long enough
| cast requires that `values` is borrowed for `'static`
...
27 | }
| - `values` dropped here while still borrowed
Look from the monomorphization side: assuming that
the compiler infers that T resolves to the concrete type &'static [u32],
and supposing that the produced code is something like:
fn unwrap_or_else_u32_sl_fn_u32_sl(slf: Option<&'static [u32]>,
f: fn() -> &'static [u32]) -> &'static [u32] {
...
}
then the above monomorphization explains the error:
slf value is optional_values: an Option<&'a [u32]> that does not live enough and clearly cannot be cast because it does not satisfies the 'static lifetime requirement.
If you write:
let _v = unwrap_or_else(optional_values, || default_values());
// the same, expliciting the return type:
let _v = unwrap_or_else(optional_values, || -> & [u32] {default_values()});
It compiles: now the lifetime of the return type is compatible with the optional_values lifetime.
Finally, I'm not able to explain why, but the evidence shows that the cast as fn() -> &'static _ helps the compiler to be sure that decoupling lifetimes bound to optional_values and default_values is safe.
There is no difference between a closure and a direct function call: It is just a matter of type inference.
closure that compiles:
let _v = optional_values.unwrap_or_else(|| default_values());
let _v = optional_values.unwrap_or_else(|| -> & [u32] {default_values()});
closure that not compiles:
let _v = unwrap_or_else(optional_values, || -> &'static [u32] {default_values()});
function that compiles:
let _v = unwrap_or_else(optional_values, default_values as fn() -> &'static _);
function that not compiles:
let _v = unwrap_or_else(optional_values, default_values);
A litte bit of explanation
Consider this equivalent code:
fn default_values() -> &'static [u32] {
static VALUES: [u32; 3] = [1, 2, 3];
&VALUES
}
fn unwrap_or_else<T, F>(slf: Option<T>, f: F) -> T where
F: FnOnce() -> T, {
match slf {
Some(t) => t,
None => f()
}
}
the following snippet:
fn main() {
let values: [u32; 3] = [4, 5, 6];
let optional_values: Option<&[u32]> = Some(&values);
let _v = unwrap_or_else(optional_values, || -> &'static [u32] {default_values});
// the above throws the same error of:
//let _v = unwrap_or_else(optional_values, default_values);
}
fails:
error[E0597]: `values` does not live long enough
--> src/main.rs:18:48
|
18 | let optional_values: Option<&[u32]> = Some(&values);
| ^^^^^^^
| |
| borrowed value does not live long enough
| cast requires that `values` is borrowed for `'static`
...
27 | }
| - `values` dropped here while still borrowed
Look from the monomorphization side: assuming that
the compiler infers that T resolves to the concrete type &'static [u32],
and supposing that the produced code is something like:
fn unwrap_or_else_u32_sl_fn_u32_sl(slf: Option<&'static [u32]>,
f: fn() -> &'static [u32]) -> &'static [u32] {
...
}
then the above monomorphization explains the error:
slf value is optional_values: an Option<&'a [u32]> that does not live enough and clearly cannot be cast because it does not satisfies the 'static lifetime requirement.
If you write:
let _v = unwrap_or_else(optional_values, || default_values());
// the same, expliciting the return type:
let _v = unwrap_or_else(optional_values, || -> & [u32] {default_values()});
It compiles: now the lifetime of the return type is compatible with the optional_values lifetime.
Finally, I'm not able to explain why, but the evidence shows that the cast as fn() -> &'static _ helps the compiler to be sure that decoupling lifetimes bound to optional_values and default_values is safe.
answered Nov 7 at 22:44
attdona
2,83411118
2,83411118
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53171108%2fwhy-do-lifetimes-differ-when-a-function-is-called-in-a-closure-vs-being-called%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
2
I don't understand why but I changed it to
default_values as fn() -> &'static _then it'll work.– DanSnow
Nov 6 at 11:48
2
Nightly +
feature(nll)gives an additional information: 'borrowed value does not live long enough, cast requires thatvaluesis borrowed for'static'– hellow
Nov 6 at 12:02
Look like the compile infer
_vbased on the return type of the function instead of infer_vbased on the type ofoptional_value, and actually onlyas fn() -> _is enough. That simply a case of "you need to help the compiler".– Stargateur
Nov 6 at 12:50