[SOLVED] Need help on understanding why this is the correct lifetime paramater


#1

So I was working on a larger project and ran into an issue. I ended up isolating it down to this smaller chunk of code:

What I don’t understand stand is the lifetime parameter on the return from the constructor on line 10. In this example why isn’t it 'a? From my understanding 'a is the lifetime of the the whole structure OutsideStruct and 'b is the lifetime of the parameter of the input. Since the input is cloned for the inside struct why would anything be tied to his lifetime?

Any help in understanding this would be great! Thanks!


#2

Note that you could also write something like this:

impl<'a> OutsideStruct<'a> {
    pub fn new(data: &'a [u8]) -> OutsideStruct<'a> {

The key point to realize, though, is that new() isn’t special; you can return a struct with any lifetime you want. Maybe this point is more obvious if I write an example with types rather than lifetimes:

struct X<T>(T);
impl<T> X<T> {
    pub fn new() -> X<i32> {
        X(0)
    }
}

#3

It’s ‘cloned’ but what you’re cloning is a reference. If you clone an &'b [u8], you get an &'b [u8]. If you want to clone the data behind the reference, and take ownership of it, you need to use the to_owned() method (which will return a Vec<u8>). Then your structs wouldn’t need lifetimes at all, but you would have to perform a clone of the data you’re taking a reference to.

You just shouldn’t call clone() here; your code will do the same thing without it, since references are Copy types just like integers are.


#4

[quote=“stusmall, post:1, topic:6480”]
'a is the lifetime of the the whole structure OutsideStruct
[/quote]I like to think of this as OutsideStruct<'a> in the impl syntax defining the type of self for functions which take self (methods). Non-methods can use arbitrary types and even if you put 'a in the signature

pub fn new(data: &'a [u8]) -> OutsideStruct<'a> {

without self there’s no variable to constraint it so it’s no different than

pub fn new<'b>(data: &'b [u8]) -> OutsideStruct<'b> {

which due to lifetime elision is identical to the following

pub fn new<'b>(data: &'b [u8]) -> OutsideStruct {
pub fn new(data: &[u8]) -> OutsideStruct {

#5

Ah ha! That explains my problem! I was actually wanting to copy of the data that is feed in, not just the reference.

So this leads to another question… Why does a to_owned() of a slice return a Vec and not another slice?


#6

A slice is what’s called an ‘dynamically sized type.’ The str type is a similar. You cannot have a [T] directly, you can only have a reference to it. The owned analog to [T] is Vec<T>, just as the owned analog to str is String.

The to_owned method is documented on this page. Unfortunately, the rustdoc output doesn’t show enough info to see that [T]::to_owned returns a Vec<T>. Here’s basically what’s going on:

pub trait ToOwned {
    // ToOwned has an associated type, which is the owned version of this type
    type Owned: Borrow<Self>;
    // ToOwned return whatever the 'Owned' type is.
    fn to_owned(&self) -> Self::Owned;
}

// There's an implementation of ToOwned for [T].
impl<T> ToOwned for [T] {
    // It defines 'Owned' as Vec<T>.
    type Owned = Vec<T>;
    fn to_owned(&self) -> Vec<T> {
        // implementation of the function
    }
}


#7

That makes a lot of sense. Thank you so much!