You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Lifetime complaints depend on whether you attach the value to a variable first #48998
fnfoo_me<'a>() -> Foo<'a>{let bar = Bar{val:42};Foo{bar:&bar
}}
With:
--> src/main.rs:15:15
|
15 | bar: &bar
| ^^^ borrowed value does not live long enough
16 | }
17 | }
| - borrowed value only lives until here
|
note: borrowed value must be valid for the lifetime 'a as defined on the function body at 10:1...
--> src/main.rs:10:1
|
10 | fn foo_me<'a>() -> Foo<'a> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
I can see what it's complaining about, but I feel like these two examples should function identically. I have a more complicated piece of code that I'm not smart enough to coerce into the working format, so there may be more complicated variants on this which are not solvable.
changed the title [-]Lifetime complaints depend on whether you attach a the value to a variable first[/-][+]Lifetime complaints depend on whether you attach the value to a variable first[/+]on Mar 13, 2018
With that, a &Bar { val: 42 } essentially turns into static BAR: Bar = Bar { val: 42 }; &BAR, so the working implementation produces a Foo<'static> ('static is what's required by the signature of foo_me). However, the broken version takes a reference to a local variable of the function, and its lifetime only lasts until the function returns, not for ever as is required by foo_me.
Also works. I wonder if it's possible to improve the error message to make this clearer. I realise we can't have every edge case documented there though, then it would make it less clear.
It's also worth mentioning that if the compiler fails to elide[1] the lifetime to static for another reason (eg the value is not allowed to be static because it's a trait or in its instantiation you're calling a function) it just silently doesn't work.
So if you add this:
implBar{fnnew(val:i32) -> Bar{Bar{val: val
}}}
And then try to do this:
fnfoo_me<'a>() -> Foo<'a>{Foo{bar:&Bar::new(42)}}
You get the error about borrowing that we got with the let example, because Rust can't handle statics that involve function calls, so it silently doesn't elide to static, so we're back to square one.
To a noob this rule in general seems really confusing, and involves you knowing all the compiler rules involved to understand why your code doesn't compile (as opposed to the error clearly explaining what you did wrong, because it's not that the borrowed value won't live long enough, it's actually because it couldn't elide it to static).
[1] Rust's usage of this word really confuses me, but that's neither here nor there
Rust's usage of (elide in the context of lifetimes) really confuses me, but that's neither here nor there
You could read it as "ensures it outlives/outlasts"/"lives long enough to survive X". Any suggestions on how to improve the wording, not only the diagnostic themselves, to make the subject friendlier to newcomers are welcome. After a while when you understand the concept is very hard to un-understand it to see the failings with fresh eyes :-/
@estebank to me elide means to omit something. So when people talk about lifetime elision, it sounds like removing lifetimes? But I think it's actually about adding implicit lifetimes? Or converting lifetimes?
I think a specific section in the handbook on what that word means would be really helpful. I imagine it's too late to change the word. I realise there is this:
The patterns programmed into Rust’s analysis of references are called the lifetime elision rules. These aren’t rules for programmers to follow; the rules are a set of particular cases that the compiler will consider, and if your code fits these cases, you don’t need to write the lifetimes explicitly.
But it's like 5 paragraphs deep into a section, and doesn't really gel with what you just said?
I find my flow for confusion is:
Make a complicated struct
Have it not compile and complain about not implementing Sized (it's not clear that I can't just implement that myself, it's just a trait like any other), oh right structs have to be a constant size
Or, ^ but with function signatures
So you change the offending element to a reference because references are constant size, now you need to add lifetimes for a reason that I don't really understand yet,
Then that in turn makes other things really complicated, because now you're not passing around the data and that causes all sorts of lifetime issues that are super complicated
I have read the chapter on lifetimes but I really feel like I need to read it 2-3 more times. My background is completely in memory-safe languages, which may make the concept more complicated for me.
It would be really cool if there was some kind of site / git repo where you progressively solve problems that are caused by lifetimes, borrowing, and generally more esoteric Rust concepts that people from major languages aren't used to. Sort of like 4clojure / project euler but specifically to get people comfortable with layering all of these techniques on top of each other.
It would be really cool if there was some kind of site / git repo where you progressively solve problems that are caused by lifetimes, borrowing, and generally more esoteric Rust concepts that people from major languages aren't used to. Sort of like 4clojure / project euler but specifically to get people comfortable with layering all of these techniques on top of each other.
Hopefully enlightenment will be in there somewhere.
Ideally though, Rust's documentation itself would contain this kind of thing, as it would be more discoverable, and if getting being noob friendly is important then I think that's critical
error[E0515]: cannot return value referencing local variable `bar`
--> src/main.rs:19:5
|
19 | / Foo {
20 | | bar: &bar
| | ---- `bar` is borrowed here
21 | | }
| |_____^ returns a value referencing data owned by the current function
For more information about this error, try `rustc --explain E0515`.
Case from comments
#[derive(Debug)]structFoo<'a>{bar:&'aBar}#[derive(Debug)]structBar{val:i32}implBar{fnnew(val:i32) -> Bar{Bar{val: val
}}}fnfoo_me<'a>() -> Foo<'a>{Foo{bar:&Bar::new(42)}}
error[E0515]: cannot return value referencing temporary value
--> src/lib.rs:19:5
|
19 | / Foo {
20 | | bar: &Bar::new(42)
| | ------------ temporary value created here
21 | | }
| |_____^ returns a value referencing data owned by the current function
For more information about this error, try `rustc --explain E0515`.
Activity
[-]Lifetime complaints depend on whether you attach a the value to a variable first[/-][+]Lifetime complaints depend on whether you attach the value to a variable first[/+]sfackler commentedon Mar 13, 2018
That's an intended result of static rvalue promotion.
With that, a
&Bar { val: 42 }essentially turns intostatic BAR: Bar = Bar { val: 42 }; &BAR, so the working implementation produces aFoo<'static>('static is what's required by the signature of foo_me). However, the broken version takes a reference to a local variable of the function, and its lifetime only lasts until the function returns, not for ever as is required by foo_me.SCdF commentedon Mar 13, 2018
Oh I see, because:
Also works. I wonder if it's possible to improve the error message to make this clearer. I realise we can't have every edge case documented there though, then it would make it less clear.
estebank commentedon Mar 14, 2018
I feel that the first case should emit a warning about lifetime
'abeing elided to'static.SCdF commentedon Mar 14, 2018
It's also worth mentioning that if the compiler fails to elide[1] the lifetime to static for another reason (eg the value is not allowed to be static because it's a trait or in its instantiation you're calling a function) it just silently doesn't work.
So if you add this:
And then try to do this:
You get the error about borrowing that we got with the
letexample, because Rust can't handle statics that involve function calls, so it silently doesn't elide to static, so we're back to square one.To a noob this rule in general seems really confusing, and involves you knowing all the compiler rules involved to understand why your code doesn't compile (as opposed to the error clearly explaining what you did wrong, because it's not that the borrowed value won't live long enough, it's actually because it couldn't elide it to static).
[1] Rust's usage of this word really confuses me, but that's neither here nor there
estebank commentedon Mar 14, 2018
You could read it as "ensures it outlives/outlasts"/"lives long enough to survive X". Any suggestions on how to improve the wording, not only the diagnostic themselves, to make the subject friendlier to newcomers are welcome. After a while when you understand the concept is very hard to un-understand it to see the failings with fresh eyes :-/
SCdF commentedon Mar 15, 2018
@estebank to me elide means to omit something. So when people talk about lifetime elision, it sounds like removing lifetimes? But I think it's actually about adding implicit lifetimes? Or converting lifetimes?
I think a specific section in the handbook on what that word means would be really helpful. I imagine it's too late to change the word. I realise there is this:
I find my flow for confusion is:
I have read the chapter on lifetimes but I really feel like I need to read it 2-3 more times. My background is completely in memory-safe languages, which may make the concept more complicated for me.
It would be really cool if there was some kind of site / git repo where you progressively solve problems that are caused by lifetimes, borrowing, and generally more esoteric Rust concepts that people from major languages aren't used to. Sort of like 4clojure / project euler but specifically to get people comfortable with layering all of these techniques on top of each other.
Ixrec commentedon Mar 16, 2018
@SCdF I think https://github.com/carols10cents/rustlings is meant to be something like this, though it hasn't made it to the "esoteric" parts of Rust yet.
SCdF commentedon Mar 16, 2018
Yep I have these links queued up:
Hopefully enlightenment will be in there somewhere.
Ideally though, Rust's documentation itself would contain this kind of thing, as it would be more discoverable, and if getting being noob friendly is important then I think that's critical
oskgo commentedon Jan 25, 2024
The diagnostics for the two examples mentioned are much nicer now after #59114:
Original case
Case from comments
I've made a new issue (#120344) for the suggestion in #48998 (comment).
It doesn't seem to me like there's much actionable left here, so I think this should be closed.
Dylan-DPC commentedon Jan 26, 2024
Closing this as it is completed and we have #120344 to track the related changes