About lifetime, why the code will have two lifetime annotations '1 and '2

struct Interface<'a> {
    manager: &'a mut Manager<'a>,
}

impl<'a> Interface<'a> {
    pub fn noop(self) {
        println!("interface consumed");
    }
}

struct Manager<'a> {
    text: &'a str,
}

struct List<'a> {
    manager: Manager<'a>,
}

impl List<'_> {
    pub fn get_interface(&mut self) -> Interface {
        Interface {
            manager: &mut self.manager,
        }
    }
}

this is the error info:

i dont know why there have two lifetime annotations '1 and '2.
can anybody help me?

Rust is telling you that after undoing lifetime elision (which is where '1 and '2 come from), it's got a problem resolving the lifetimes in List::get_interface.

First, I turn on #![deny(elided_lifetimes_in_paths)]; this reveals that pub fn get_interface(&mut self) -> Interface should in fact be pub fn get_interface(&mut self) -> Interface<'_>, but that change does not fix the problem, although it makes the error message clearer.

I can then make the lifetimes Rust points out explicit, to get:

impl<'manager> List<'manager> {
    pub fn get_interface<'this>(&'this mut self) -> Interface<'this> {
        Interface {
            manager: &mut self.manager,
        }
    }
}

And at this point, the error message explains how to fix it.

With that said, this looks like you're digging yourself a hole trying to do something that you don't fully understand how to model in Rust. Data structures with lifetime parameters are something that you should only rarely need; they are useful for cases where you're writing a "view struct" over some owned data, but you should normally aim to have your data structures own everything that you use, rather than borrow it from elsewhere, because borrows are at their best when they're short-lived.

At a minimum, I'd make text a String, not &'a str, getting me to this Playground which is already considerably simpler; I'd also question whether Interface should borrow the manager, or own it; if it can own it (getting me to this Playground), all sorts of things become simpler when dealing with Interfaces

2 Likes

This is equivalent to writing the following:

impl<'a> List<'a> {
    pub fn get_interface<'b>(&'b mut self) -> Interface<'b> {
        Interface {
            manager: &mut self.manager,
        }
    }
}

You are creating an Interface<'b>, which requires a &'b mut Manager<'b>, however the expression &mut self.manager has type &'b mut Manager<'a>, since you are reborrowing from self, which is a reference with lifetime 'b, and hence you get a reference with lifetime 'b, however self points to a List<'a>, whose field has type Manager<'a>.


The fundamental issue is that Interface is trying to hold a &'a mut Manager<'a>. Types like this, where the lifetime of a mutable reference is equal to the lifetime of the pointed type, are never what you want. Attempting to create such references will end up borrowing your object forever.

A relative simple fix to this would be changing your Interface type to hold two lifetimes:

struct Interface<'a, 'b> {
    manager: &'b mut Manager<'a>,
}

Then your List impl can be written like this:

impl<'a> List<'a> {
    pub fn get_interface<'b>(&'b mut self) -> Interface<'a, 'b> {
        Interface {
            manager: &mut self.manager,
        }
    }
}

You should also try avoiding creating structs that hold references like these if you can, as they can easily result in complex errors, especially for beginners.

4 Likes

Thank you, I know how to fix it now

Is List<'a> not a whole entity, but rather a lifetime annotation for &mut self.manager?

I'm not sure what you mean, List<'a> is a type.

is that using List<'a> instead of List<'_> means 'a will annotate &mut self.manager to &mut Manager<'a>

The only difference between impl<'a> List<'a> and impl List<'_> is that the first one gives a name to the lifetime which you can use later to refer to that same lifetime. Except from this they are the same.

1 Like

thank you very much, i already totally understand why the code will break and how to fix it.
And,
I would like to know how you learned Rust. The tutorial I am currently reading does not mention The fundamental issue. I only found out about &'a mut Thing<'a> after reading the link [quote="SkiFire13, post:4, topic:117692"]
borrowing your object forever
[/quote],
so do you have a recommended Rust learning path
(I used a translation tool because my English is terrible, but it seems that the translation is also quite terrible)

I initially mainly learned it though the official book and then picked up some more advanced knowledge through experience writing Rust code.

Unfortunately I don't remember exactly where I first saw the &'a mut Thing<'a> issue, maybe I somehow managed to avoid it until I became proficient enough to intuitively notice that it can't possibly work.

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.