E0597 error when implementing Clone


#1

Hi all,

I can’t get out of this error:

error[E0597]: `cloned` does not live long enough
   --> src/noc3.rs:185:9
    |
185 |         cloned.push(e);
    |         ^^^^^^ does not live long enough
...
189 |     }
    |     - borrowed value only lives until here
    |
note: borrowed value must be valid for the lifetime 'a as defined on the impl at 170:1...
   --> src/noc3.rs:170:1
    |
170 | / impl<'a, Element: Nameable + Clone> Clone for NamedObjectsContainer<Element, &'a Element> {
171 | |
172 | |     fn clone(&self) -> NamedObjectsContainer<Element, &'a Element> {
173 | |         let cloned = NamedObjectsContainer::<Element, &Element>::new();
...   |
189 | |     }
190 | | }
    | |_^

for this:

impl<'a, Element: Nameable + Clone> Clone for S<Element, &'a Element> {

    fn clone(&self) -> S<Element, &'a Element> {
        let cloned = S::<Element, &Element>::new();
            
        let e = self.list.get(0).unwrap().clone();

        cloned.push(e);


        cloned
    }
}

I don’t understand why cloned object is living for the whole impl (that’s the hint the error is giving) and borrow is not ending at the end of clone(). S is a container structure like a Vec:

pub struct S<Element, Collection> {
    /// List of ELement structs
    pub list: Vec<Element>,
    /// Hashmap keeping track of the name vs. index of the structure in the previous list
    pub hmap: HashMap<String, Collection>,
}

and push() is adding an element.

Thanks for any help.


#2

cloned lives for the whole impl, and beyond, because you’re returning it. But your new is using an anonymous &Element lifetime. Does it work if you change that to &'a Element to match the return type?


#3

Other than needing to change cloned to mut cloned, I had no trouble compiling your snippet.


#4

Actually, I didn’t put the whole code.

Here it is: https://play.rust-lang.org/?gist=3c369b569af26f3d0c4f0bd51a8f3444&version=undefined

and do not compile.


#5

If push takes &'a mut self, then this becomes kind of a permanent borrow, for the remaining 'a life of the type.

Your type looks like you’re trying to create something self-referential, which is not generally supported. If hmap wants to hold references into list, any change to the list like pushing a new item will invalidate all those references. One option might be to map to indexes instead.


#6

Thanks @cuviper , indeed if the Vec is reallocated, all references are wrong, right?
So using a linked list will solve the problem ?

If not, I’ll keep indices instead of references.


#7

But if I use the hashmap to store indexes, I can’t use the Index trait as I would like. I’d like the Index to return a reference on a vector of references or a reference.

The Index trait can’t return T but &T only.


#8

A linked list would solve the specific problem of reallocation, but the borrow checker isn’t analyzing that deeply. That signature of push will cause the same problem no matter what the underlying types and code are actually doing.


#9

I don’t understand why Index is a problem. Get the usize index from the hash map, then use that to get the reference from the vector.


#10

Yes but my struct is meant to be used with a reference or a vector of references. And in that case, I can’t return a reference on a vector of references by if I use only indexes.


#11

I am just a novice, but it seems that you want to store a reference to the vector in addition to indexes.


#12

@nakacristo thanks. But I thought it was more efficient to store only refs.


#13

Actually, I decided to only use Vec and a HashMap of indexes, giving up the idea of implementing the Index trait with a &str, and implementing it only for an index.