Return reference with lifetime of self - rust

I'd like to write some code like the following:
struct Foo {
foo: usize
}
impl Foo {
pub fn get_foo<'a>(&'a self) -> &'self usize {
&self.foo
}
}
But this doesn't work, failing with invalid lifetime name: 'self is no longer a special lifetime.
How can I return a reference that lives as long as the object itself?

You don't want the reference to live exactly as long as the object. You just want a borrow on the object (quite possibly shorter than the entire lifetime of the object), and you want the resulting reference to have the lifetime of that borrow. That's written like this:
pub fn get_foo<'a>(&'a self) -> &'a usize {
&self.foo
}
Additionally, lifetime elision makes the signature prettier:
pub fn get_foo(&self) -> &usize {
&self.foo
}

In your example the lifetime of self is 'a so the lifetime of the returned reference should be 'a:
pub fn get_foo<'a>(&'a self) -> &'a usize {
&self.foo
}
However the compiler is able to deduce (lifetime elision) the correct lifetime in simple cases like that, so you can avoid to specify lifetime at all, this way:
pub fn get_foo(&self) -> &usize {
&self.foo
}
Look here for lifetime elision rules

Related

Lifetime gets smaller after extracting method

I had a lifetime problem on extracting a method in Rust. Following is a minimal example:
pub struct Obj {
value: usize,
}
pub struct Container<'a> {
content: &'a Obj,
}
pub struct Props<'a> {
att: Container<'a>,
}
impl <'a> Props<'a> {
pub fn value(&self) -> usize {
self.att.content.value
}
}
This works fine with:
pub fn test<'a>(properties: Props<'a>) -> impl (Fn() -> usize) + 'a {
|| properties.att.content.value
}
but gives an error for:
pub fn test<'a>(properties: Props<'a>) -> impl (Fn() -> usize) + 'a {
|| properties.value()
}
closure may outlive the current function, but it borrows 'properties', which is owned by the current function
I think I understand this error message, but I do not understand why the first piece of code does compile. Isn't properties borrowed also in the first example?
Could this problem be solved by putting some lifetimes to the function value()?
Link to Rust Playground Example
Since edition 2021 closures are allowed to borrow only part of a struct so your first example is allowed to borrow only part of it namely properties.att.content.value which is behind a reference of appropriate lifetime for the closure you're returning.
The second example does not see it only needs that part so it has to borrow the full properties, but that is dropped at the end of the function, so you can't return a closure referencing it.
To make the second example work just follow the compilers recommendation, add a move before the closure, that moves properties inside the closure so it can safely be returned.
pub fn test<'a>(properties: Props<'a>) -> impl (Fn() -> usize) + 'a {
move || properties.value()
}

Rust lifetimes for implementing a trait on nested slices

I want to create a wrapper around (nested) slices for easy operations on multidimensional data, owned by a different struct.
The most basic version of the mutable version of my slice wrapper might look like this:
struct MySliceMut<'a> {
data: Vec<&'a mut [f32]>,
}
impl<'a, 'b> MySliceMut<'a> {
fn get(&'b mut self) -> &'a mut [&'b mut [f32]] {
self.data.as_mut_slice()
}
}
Now if I want to implement a trait, for instance AddAssign, Rust does not seem to infer the lifetime of &mut self from the implementing type. The compiler complains that &mut self might outlive 'a:
impl<'a> AddAssign<MySlice<'a>> for MySliceMut<'a> { // lifetime 'a
fn add_assign(&mut self, rhs: MySlice<'a>) { // lifetime '1
let a = self.get(); // lifetime may not live long enough, '1 must outlive 'a
let b = rhs.get();
// do inplace addition here
}
}
Full Code - Rust Playground
I tried to figure out the issue with the lifetimes, but can't find it. Would the trait impl require any additional annotations?
struct MySlice<'a> {
data: Vec<&'a [f32]>,
}
impl<'a, 'b> MySlice<'a> {
fn get(&'b self) -> &'a [&'b [f32]] {
self.data.as_slice()
}
}
Problem with your code is that fn get(&'b self) returns variable with wrong lifetime. Associated lifetime 'a of MySlice<'a> is lifetime of inner slice. Associated lifetime 'b of fn get(...) is lifetime of the self. So I guess the function probably should return &'b [&'a [f32]] instead.
-- Edited --
Make sure to change fn get(...) of MySliceMut either.

How to make a lifetime constraint "pass through" from object to its grandchild?

I'm doing some experiments with lifetimes and the borrow checker. Imagine this first struct:
struct First {}
impl First {
fn new() -> Self {
Self {}
}
fn second(&self) -> Second {
Second::new(self)
}
fn hello(&self) {
println!("Hello");
}
}
And the second, which has a lifetime constraint that depends on First:
struct Second<'a> {
owner: &'a First,
}
impl<'a> Second<'a> {
fn new(owner: &'a First) -> Self {
Self { owner }
}
fn hello(&self) {
self.owner.hello();
}
}
The code above works perfectly fine: Second is created by First, and it cannot outlive First.
The problem
Now let's modify Second so it can create a third struct, Third:
struct Second<'a> {
owner: &'a First,
}
impl<'a> Second<'a> {
fn new(owner: &'a First) -> Self {
Self { owner }
}
fn third(&self) -> Third {
Third::new(self.owner)
}
fn hello(&self) {
self.owner.hello();
}
}
And Third itself, which also depends on First:
struct Third<'a> {
owner: &'a First,
}
impl<'a> Third<'a> {
fn new(owner: &'a First) -> Self {
Self { owner }
}
fn hello(&self) {
self.owner.hello();
}
}
I would imagine that, when creating an instance of Third, it would depend on First, but that's not the case. Actually Third depends on Second:
fn main() {
let f = First::new();
let t = {
let sss = f.second();
sss.third() // error: sss does not live long enough
};
}
So, how can I make the lifetime constraint "pass through" from First to Third?
Full playground.
Rust has rules how it infers lifetimes for functions: the lifetime elision rules.
And those rules state that:
Each elided lifetime (i.e. a type that should had have a lifetime, but doesn't, like &T that is actually &'a T) in the parameters becomes a distinct lifetime parameter.
If there is exactly one lifetime used in the parameters (elided or not), that lifetime is assigned to all elided output lifetimes.
In method signatures there is another rule
If the receiver has type &Self or &mut Self, then the lifetime of that reference to Self is assigned to all elided output lifetime parameters.
Let's take First::second() as an example. Its signature is:
fn second(&self) -> Second
Or, with all lifetimes explicitly elided (by the way, it is considered a good practice to explictly elide all lifetimes that are not on references, like Second<'_> in this example):
fn second(&'_ self) -> Second<'_>
So according to rule #1 we assign a new lifetime, let's call it 'a, to &self:
fn second<'a>(&'a self) -> Second<'_>
Now, according to rule #3, we pick 'a for Second<'_>:
fn second<'a>(&'a self) -> Second<'a>
That is, we return Second that has the same lifetime as the reference to self.
Now let's apply it to Second::third()...
fn third(&self) -> Third
// Becomes
fn third<'b>(&'b self) -> Third<'b> // Lifetime `'a` is already used
But this is not what we want! We want the resulting Third to depend on the lifetime of our contained First instance, not on the lifetime of &self! So what we really need is to use Third<'a>:
fn third(&self) -> Third<'a> { ... }
Now it works beautifully.
You just need to specify the same lifetime of the inner reference in Second when creating Third:
impl<'a> Second<'a> {
fn new(owner: &'a First) -> Self {
Self { owner }
}
fn third(&self) -> Third<'a> {
Third::new(self.owner)
}
fn hello(&self) {
self.owner.hello();
}
}
Playground
It is the same as when creating Second from first:
impl First {
...
fn second(&self) -> Second {
Second::new(self)
}
...
}
Here, second is bound to a 'self lifetime, that is, it can live as much as that &self First instance. But in this case the compiler specify it for you.

Rust lifetime error

Can anyone tell what the lifetime error is in the following code? (simplified from my actual code) I've looked it over myself, but I can't figure out what is wrong or how to fix it. The problem comes when I try to add the Cell, but I'm not sure why.
use std::cell::Cell;
struct Bar<'a> {
bar: &'a str,
}
impl<'a> Bar<'a> {
fn new(foo: &'a Foo<'a>) -> Bar<'a> { Bar{bar: foo.raw} }
}
pub struct Foo<'a> {
raw: &'a str,
cell: Cell<&'a str>,
}
impl<'a> Foo<'a> {
fn get_bar(&self) -> Bar { Bar::new(&self) }
}
The compiler error is
error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'a` due to conflicting requirements
--> src/foo.rs:15:32
|
15 | fn get_bar(&self) -> Bar { Bar::new(&self) }
| ^^^^^^^^
First, the solution:
use std::cell::Cell;
struct Bar<'a> {
bar: &'a str,
}
impl<'a> Bar<'a> {
fn new(foo: &Foo<'a>) -> Bar<'a> { Bar{bar: foo.raw} }
}
pub struct Foo<'a> {
raw: &'a str,
cell: Cell<&'a str>,
}
impl<'a> Foo<'a> {
fn get_bar(&self) -> Bar<'a> { Bar::new(&self) }
}
There are two problems in your code. The first is with get_bar, where you didn't specify the lifetime for the return type. When you don't specify lifetimes in signatures, Rust doesn't infer the correct lifetimes, it just blindly fills them in based on simple rules. In this specific case, what you get is effectively fn get_bar<'b>(&'b self) -> Bar<'b> which is obviously wrong, given the lifetime of self.raw (which was what you actually wanted) is 'a. See the Rust Book chapter on Lifetime Elision.
The second problem is that you're over-constraining the argument to Bar::new. &'a Foo<'a> means you require a borrow to a Foo for as long as the strings it's borrowing exist. But borrows within a type have to outlive values of said type, so the only lifetime valid in this case is where 'a matches the entire lifetime of the thing being borrowed... and that conflicts with the signature of get_bar (where you're saying &self won't necessarily live as long as 'a since it has its own lifetime). Long story short: remove the unnecessary 'a from the Foo borrow, leaving just &Foo<'a>.
To rephrase the above: the problem with get_bar was that you hadn't written enough constraints, the problem with Bar::new was that you'd written too many.
DK explained which constraints were missing and why, but I figured I should explain why the code was working before I added Cell. It turns out to be due to variance inference.
If you add in the inferred lifetimes to the original code and rename the lifetime variables to be unique, you get
struct Bar<'b> {
bar: &'b str,
}
impl<'b> Bar<'b> {
fn new(foo: &'b Foo<'b>) -> Bar<'b> { Bar{bar: foo.raw} }
}
pub struct Foo<'a> {
raw: &'a str,
cell: Cell<&'a str>,
}
impl<'a> Foo<'a> {
fn get_bar<'c>(&'c self) -> Bar<'c> { Bar::new(&self) }
}
The problem comes when calling Bar::new, because you are passing &'c Foo<'a>, to something expecting &'b Foo<'b>. Normally, immutable types in Rust are covariant, meaning that &Foo<'a> is implicitly convertable to &Foo<'b> whenever 'b is a shorter lifetime than 'a. Without the Cell, &'c Foo<'a> converts to &'c Foo<'c>, and is passed to Bar::new wiht 'b = 'c, so there is no problem.
However, Cell adds interior mutability to Foo, which means that it is no longer safe to be covariant. This is because Bar could potentially try to assign a shorter lived 'b reference back into the original Foo, but Foo requires that all of the references it holds are valid for the longer lifetime 'a. Therefore, interior mutability makes &Foo invariant, meaning you can no longer shorten the lifetime parameter implicitly.

Tying a trait lifetime variable to &self lifetime

I'd like to do something along the following lines:
trait GetRef<'a> {
fn get_ref(&self) -> &'a [u8];
}
struct Foo<'a> {
buf: &'a [u8]
}
impl <'a> GetRef<'a> for Foo<'a> {
fn get_ref(&self) -> &'a [u8] {
&self.buf[1..]
}
}
struct Bar {
buf: Vec<u8>
}
// this is the part I'm struggling with:
impl <'a> GetRef<'a> for Bar {
fn get_ref(&'a self) -> &'a [u8] {
&self.buf[1..]
}
The point of the explicit lifetime variable in the GetRef trait is to allow the return value of get_ref() on a Foo object to outlive the Foo itself, tying the return value's lifetime to that of the lifetime of Foo's buffer.
However, I haven't found a way to implement GetRef for Bar in a way that the compiler accepts. I've tried several variations of the above, but can't seem to find one that works. Is there any there any reason that this fundamentally cannot be done? If not, how can I do this?
Tying a trait lifetime variable to &self lifetime
Not possible.
Is there any there any reason that this fundamentally cannot be done?
Yes. An owning vector is something different than a borrowed slice. Your trait GetRef only makes sense for things that already represent a “loan” and don't own the slice. For an owning type like Bar you can't safely return a borrowed slice that outlives Self. That's what the borrow checker prevents to avoid dangling pointers.
What you tried to do is to link the lifetime parameter to the lifetime of Self. But the lifetime of Self is not a property of its type. It just depends on the scope this value was defined in. And that's why your approach cannot work.
Another way of looking at it is: In a trait you have to be explicit about whether Self is borrowed by a method and its result or not. You defined the GetRef trait to return something that is not linked to Self w.r.t. lifetimes. So, no borrowing. So, it's not implementable for types that own the data. You can't create a borrowed slice referring to a Vec's elements without borrowing the Vec.
If not, how can I do this?
Depends on what exactly you mean by “this”. If you want to write a “common denominator” trait that can be implemented for both borrowed and owning slices, you have to do it like this:
trait GetRef {
fn get_ref(&self) -> &[u8];
}
The meaning of this trait is that get_ref borrows Self and returns a kind of “loan” because of the current lifetime elision rules. It's equivalent to the more explicit form
trait GetRef {
fn get_ref<'s>(&self) -> &'s [u8];
}
It can be implemented for both types now:
impl<'a> GetRef for Foo<'a> {
fn get_ref(&self) -> &[u8] { &self.buf[1..] }
}
impl GetRef for Bar {
fn get_ref(&self) -> &[u8] { &self.buf[1..] }
}
You could make different lifetimes for &self and result in your trait like that:
trait GetRef<'a, 'b> {
fn get_ref(&'b self) -> &'a [u8];
}
struct Foo<'a> {
buf: &'a [u8]
}
impl <'a, 'b> GetRef<'a, 'b> for Foo<'a> {
fn get_ref(&'b self) -> &'a [u8] {
&self.buf[1..]
}
}
struct Bar {
buf: Vec<u8>
}
// Bar, however, cannot contain anything that outlives itself
impl<'a> GetRef<'a, 'a> for Bar {
fn get_ref(&'a self) -> &'a [u8] {
&self.buf[1..]
}
}
fn main() {
let a = vec!(1 as u8, 2, 3);
let b = a.clone();
let tmp;
{
let x = Foo{buf: &a};
tmp = x.get_ref();
}
{
let y = Bar{buf: b};
// Bar's buf cannot outlive Bar
// tmp = y.get_ref();
}
}

Resources