How to retrieve a user-defined type from a for loop? - reference

I defined an Attribute type and I have a Vec<Attribute> that I am looping over to retrieve the "best" one. This was similar to my first attempt:
#[derive(Debug)]
struct Attribute;
impl Attribute {
fn new() -> Self {
Self
}
}
fn example(attrs: Vec<Attribute>, root: &mut Attribute) {
let mut best_attr = &Attribute::new();
for a in attrs.iter() {
if is_best(a) {
best_attr = a;
}
}
*root = *best_attr;
}
// simplified for example
fn is_best(_: &Attribute) -> bool {
true
}
I had the following compile error:
error[E0507]: cannot move out of borrowed content
--> src/lib.rs:17:13
|
17 | *root = *best_attr;
| ^^^^^^^^^^ cannot move out of borrowed content
After some searching for a solution, I resolved the error by doing the following:
Adding a #[derive(Clone)] attribute to my Attribute struct
Replacing the final statement with *root = best_attr.clone();
I don't fully understand why this works, and I feel like this is a rough solution to the problem I was having. How does this resolve the error, and is this the correct way to solve this problem?

You are experiencing the basis of the Rust memory model:
every object can (and must!) be owned by only exactly one other object
most types are never implicitly copied and always moved (there are some exceptions: types that implement Copy)
Take this code for example:
let x = String::new();
let y = x;
println!("{}", x);
it generates the error:
error[E0382]: borrow of moved value: `x`
--> src/main.rs:4:20
|
3 | let y = x;
| - value moved here
4 | println!("{}", x);
| ^ value borrowed here after move
|
= note: move occurs because `x` has type `std::string::String`, which does not implement the `Copy` trait
x, of type String, is not implicitly copyable, and thus has been moved into y. x cannot be used any longer.
In your code, when you write *root = *best_attr, you are first dereferencing the reference best_attr, then assigning the dereferenced value to *root. Your Attribute type is not Copy, thus this assignment should be a move.
Then, the compiler complains:
cannot move out of borrowed content
Indeed, best_attr is an immutable reference, which does not allow you to take ownership of the value behind it (it doesn't even allow modifying it). Allowing such a move would put the object owning the value behind the reference in an undefined state, which is exactly what Rust aims to prevent.
In this case, your best option is indeed to create a new object with the same value as the first one, which is exactly what the trait Clone is made for.
#[derive(Clone)] allows you to mark your structs as Clone-able, as long as all of their fields are Clone. In more complex cases, you'll have to implement the trait by hand.

Related

Is this the idiomatic way to make self-referential structures?

I am interested in knowing the idiomatic/canonical way of making self-referential structures in Rust. The related question Why can't I store a value and a reference to that value in the same struct explains the problem, but try as I might, I couldn't figure out the answer in the existing question (although there were some useful hints).
I have come up with a solution, but I am unsure of how safe it is, or if it is the idiomatic way to solve this problem; if it isn't, I would very much like to know what the usual solution is.
I have an existing structure in my program that holds a reference to a sequence. Sequences hold information about chromosomes so they can be rather long, and copying them isn't a viable idea.
// My real Foo is more complicated than this and is an existing
// type I'd rather not have to rewrite if I can avoid it...
struct Foo<'a> {
x: &'a [usize],
// more here...
}
impl<'a> Foo<'a> {
pub fn new(x: &'a [usize]) -> Self {
Foo {
x, /* more here... */
}
}
}
I now need a new structure that reduces the sequence to something smaller and then builds a Foo structure over the reduced string, and since someone has to own both reduced string and Foo object, I would like to put both in a structure.
// My real Bar is slightly more complicated, but it boils down to having
// a vector it owns and a Foo over that vector.
struct Bar<'a> {
x: Vec<usize>,
y: Foo<'a>, // has a reference to &x
}
// This doesn't work because x is moved after y has borrowed it
impl<'a> Bar<'a> {
pub fn new() -> Self {
let x = vec![1, 2, 3];
let y = Foo::new(&x);
Bar { x, y }
}
}
Now, this doesn't work because the Foo object in a Bar refers into the Bar object
and if the Bar object moves, the reference will point into memory that is no longer occupied by the Bar object
To avoid this problem, the x element in Bar must sit on the heap and not move around. (I think the data in a Vec already sits happily on the heap, but that doesn't seem to help me here).
A pinned box should do the trick, I belive.
struct Bar<'a> {
x: Pin<Box<Vec<usize>>>,
y: Foo<'a>,
}
Now the structure looks like this
and when I move it, the references point to the same memory.
However, moving x to the heap isn't enough for the type-checker. It still thinks that moving the pinned box will move what it points to.
If I implement Bar's constructor like this:
impl<'a> Bar<'a> {
pub fn new() -> Self {
let v: Vec<usize> = vec![1, 2, 3];
let x = Box::pin(v);
let y = Foo::new(&x);
Bar { x, y }
}
}
I get the error
error[E0515]: cannot return value referencing local variable `x`
--> src/main.rs:22:9
|
21 | let y = Foo::new(&x);
| -- `x` is borrowed here
22 | Bar { x, y }
| ^^^^^^^^^^^^ returns a value referencing data owned by the current function
error[E0505]: cannot move out of `x` because it is borrowed
--> src/main.rs:22:15
|
17 | impl<'a> Bar<'a> {
| -- lifetime `'a` defined here
...
21 | let y = Foo::new(&x);
| -- borrow of `x` occurs here
22 | Bar { x, y }
| ------^-----
| | |
| | move out of `x` occurs here
| returning this value requires that `x` is borrowed for `'a`
Some errors have detailed explanations: E0505, E0515.
For more information about an error, try `rustc --explain E0505`.
(Playground)
Even though the object I take a reference of sits on the heap, and doesn't move, the checker still sees me borrowing from an object that moves, and that, of course, is a no-no.
Here, you might stop and notice that I am trying to make two pointers to the same object, so Rc or Arc is an obvious solution. And it is, but I would have to change the implementation of Foo to have an Rc member instead of a reference. While I do have control of the source code for Foo, and I could update it and all the code that uses it, I am reluctant to make such a major change if I can avoid it. And I could have been in a situation where I am not in control of the Foo, so I couldn't change its implementation, and I would love to know how I would solve that situation then.
The only solution I could get to work was to get a raw pointer to x, so the type-checker doesn't see that I borrow it, and then connect x and y though that.
impl<'a> Bar<'a> {
pub fn new() -> Self {
let v: Vec<usize> = vec![1, 2, 3];
let x = Box::new(v);
let (x, y) = unsafe {
let ptr: *mut Vec<usize> = Box::into_raw(x);
let w: &Vec<usize> = ptr.as_ref().unwrap();
(Pin::new(Box::from_raw(ptr)), Foo::new(&w))
};
Bar { x, y }
}
}
Playground code here
What I don't know is if this is the right way to do it. It seems rather complicated, but perhaps it is the only way to make a structure like this in Rust? That some sort of unsafe is needed to trick the compiler. So that is the first of my questions.
The second is, if this is safe to do? Of course it is unsafe in the technical sense, but am I risking creating a reference to memory that might not be valid later? It is my impression that Pin should guarantee that the object remains where it is supposed to sit, and that the lifetime of the Bar<'a> and Foo<'a> objects should ensure that the reference doesn't out-live the vector, but once I have gone unsafe, could that promise be broken?
Update
The owning_ref crate has functionality that looks like what I need. You can create owned objects that present their references as well.
There is an OwningRef type that wraps an object and a reference, and it would be wonderful if you could have the slice in that and getting the reference wasn't seen as borrowing from the object, but obviously that isn't the case. Code such as this
use owning_ref::OwningRef;
struct Bar<'a> {
x: OwningRef<Vec<usize>, [usize]>,
y: Foo<'a>, // has a reference to &x
}
// This doesn't work because x is moved after y has borrowed it
impl<'a> Bar<'a> {
pub fn new() -> Self {
let v: Vec<usize> = vec![1, 2, 3];
let x = OwningRef::new(v);
let y = Foo::new(x.as_ref());
Bar { x, y }
}
}
you get the error
error[E0515]: cannot return value referencing local variable `x`
--> src/main.rs:22:9
|
21 | let y = Foo::new(x.as_ref());
| ---------- `x` is borrowed here
22 | Bar { x, y }
| ^^^^^^^^^^^^ returns a value referencing data owned by the current function
error[E0505]: cannot move out of `x` because it is borrowed
--> src/main.rs:22:15
|
17 | impl<'a> Bar<'a> {
| -- lifetime `'a` defined here
...
21 | let y = Foo::new(x.as_ref());
| ---------- borrow of `x` occurs here
22 | Bar { x, y }
| ------^-----
| | |
| | move out of `x` occurs here
| returning this value requires that `x` is borrowed for `'a`
Some errors have detailed explanations: E0505, E0515.
For more information about an error, try `rustc --explain E0505`.
error: could not compile `foo` due to 2 previous errors
The reason is the same as before: I borrow a reference to x and then I move it.
There are different wrapper objects in the crate, and in various combinations they will let me get close to a solution and then snatch it away from me, because what I borrow I still cannot move later, e.g.:
use owning_ref::{BoxRef, OwningRef};
struct Bar<'a> {
x: OwningRef<Box<Vec<usize>>, Vec<usize>>,
y: Foo<'a>, // has a reference to &x
}
// This doesn't work because x is moved after y has borrowed it
impl<'a> Bar<'a> {
pub fn new() -> Self {
let v: Vec<usize> = vec![1, 2, 3];
let v = Box::new(v); // Vector on the heap
let x = BoxRef::new(v);
let y = Foo::new(x.as_ref());
Bar { x, y }
}
}
error[E0515]: cannot return value referencing local variable `x`
--> src/main.rs:23:9
|
22 | let y = Foo::new(x.as_ref());
| ---------- `x` is borrowed here
23 | Bar { x, y }
| ^^^^^^^^^^^^ returns a value referencing data owned by the current function
error[E0505]: cannot move out of `x` because it is borrowed
--> src/main.rs:23:15
|
17 | impl<'a> Bar<'a> {
| -- lifetime `'a` defined here
...
22 | let y = Foo::new(x.as_ref());
| ---------- borrow of `x` occurs here
23 | Bar { x, y }
| ------^-----
| | |
| | move out of `x` occurs here
| returning this value requires that `x` is borrowed for `'a`
Some errors have detailed explanations: E0505, E0515.
For more information about an error, try `rustc --explain E0505`.
I can get around this by going unsafe and work with a pointer, of course, but then I am back to the solution I had with Pin and pointer hacking. I strongly feel that there is a solution here, (especially because having a Box<Vec<...>> and the corresponding Vec<...> isn't adding much to the table so there must be more to the crate), but what it is is eluding me.
(I think the data in a Vec already sits happily on the heap, but that doesn't seem to help me here).
Indeed the data in a Vec does already sit on the heap, and the x: &'a [usize] in Foo is already a reference to that heap allocation; so your problem here is not (as shown in your graphics) that moving Bar would result in (the undefined behaviour of) a dangling reference.
However, what happens if the Vec were to outgrow its current allocation? It would reallocate and be moved from its present heap allocation to another—and this would result in a dangling reference. Hence the borrow checker must enforce that, so long as anything (e.g. a Foo) that borrows from the Vec exists, the Vec cannot be mutated. Yet here we already have an expressivity problem: the Rust language has no way to annotate Bar to indicate this relationship.
Your proposed unsafe solution uses <*mut _>::as_ref, whose safety documentation includes the following requirement (emphasis added):
You must enforce Rust’s aliasing rules, since the returned lifetime 'a is arbitrarily chosen and does not necessarily reflect the actual lifetime of the data. In particular, for the duration of this lifetime, the memory the pointer points to must not get mutated (except inside UnsafeCell).
This is the key bit of the compiler's safety checks that you are trying to opt out of—but because accessing Bar now requires that one uphold this requirement, you do not have a completely safe abstraction. In my view, a raw pointer would be a tad safer here because it forces one to consider the safety of every access.
For example, one issue that immediately springs to mind is that x is declared before y in Bar and therefore, upon destruction, it will be dropped first: the Vec's heap allocation will be freed while Foo still holds references into it: undefined behaviour! Simply reordering the fields would avoid this particular problem, but there would have been no such problem with raw pointers (and any attempt to dereference them in Foo's drop handler would have forced one to consider whether they were still dereferenceable at that time).
Personally, I would try to avoid self-referencing here and probably use an arena.
I think I have finally grokked ouroboros and that is an elegant solution.
You use a macro, self_referencing when defining a structure, and inside the structure you can specify that one entry borrows others. For my application, I got it to work like this:
use ouroboros::self_referencing;
#[self_referencing]
struct _Bar {
x: Vec<usize>,
#[borrows(x)]
#[covariant]
y: Foo<'this>,
}
struct Bar(pub _Bar);
The y element references x, so I specify that. I'm sure why co-/contra-varianse is needed in this particular case where there is only one lifetime, but it specififes whether other references should live longer or can live shorter than the object. I've defined the struct as _Bar and then wrapped it in Bar. This is because macro will create a new method, and I don't want the default one. At the same time I wnat to call my constructor new to stick with tradition. So I wrap the type and write my own constructor:
impl Bar {
pub fn new() -> Self {
let x: Vec<usize> = vec![1, 2, 3];
let _bar = _BarBuilder {
x,
y_builder: |x: &Vec<usize>| Foo::new(&x),
}
.build();
Bar(_bar)
}
}
I don't use the generated _Bar::new but a generated _BarBuilder object where I can specify how to get the y value from the x reference.
I have also written accessors to get the two values. There isn't anything special here.
impl Bar {
pub fn x(&self) -> &Vec<usize> {
self.0.borrow_x()
}
pub fn y(&self) -> &Foo {
self.0.borrow_y()
}
}
and with that my trivial little test case runs...
fn main() {
let bar = Bar::new();
let vec = bar.x();
for &i in vec {
println!("i == {}", i);
}
let vec = bar.y().x;
for &i in vec {
println!("i == {}", i);
}
}
This is probably the best solution so far, assuming that there are no hidden costs that I am currently unaware of.

solving "argument requires that `x` is borrowed for `'y`"

tl;dr blocked by "argument requires that `x` is borrowed for `'y`"; how can I coerce variable x to lifetime 'y?
Given the following code, I'm blocked when trying to create the Box pointer pointing to a reference. I know the referenced object instance lives long enough. However, the rust compiler is concerned it does not.
How do I tell the rust compiler that Box::new(&thing) is valid for the lifetime of the containing struct instance?
Code example
This code is essentially:
Things (a Vec), holding multiple Things
counter of Things (a HashMap), holding multiple Box pointers to different &Thing as keys, a u64 count as values
(My best attempt at) a minimal example (rust playground):
use std::collections::HashMap;
type Thing<'a> = (&'a str, usize);
type Things<'a> = Vec<Thing<'a>>;
type ThingsCounterKey<'a> = Box<&'a Thing<'a>>;
type ThingsCounter<'a> = HashMap<ThingsCounterKey<'a>, u64>;
pub struct Struct1<'struct1> {
things1: Things<'struct1>,
thingscounter1: ThingsCounter<'struct1>,
}
impl<'struct1> Struct1<'struct1> {
pub fn new() -> Struct1<'struct1> {
Struct1{
things1: Things::new(),
thingscounter1: ThingsCounter::new(),
}
}
fn things_update(&mut self, thing_: Thing<'struct1>) {
self.things1.push(thing_);
let counter = self.thingscounter1.entry(
ThingsCounterKey::new(&thing_)
).or_insert(0);
*counter += 1;
}
}
fn main() {
let mut s1 = Struct1::new();
for (x, y) in [("a", 1 as usize), ("b", 2 as usize)] {
s1.things_update((x, y));
}
}
This results in compiler error:
error[E0597]: `thing_` does not live long enough
--> src/main.rs:24:35
|
14 | impl<'struct1> Struct1<'struct1> {
| -------- lifetime `'struct1` defined here
...
24 | ThingsCounterKey::new(&thing_)
| ----------------------^^^^^^^-
| | |
| | borrowed value does not live long enough
| argument requires that `thing_` is borrowed for `'struct1`
...
27 | }
| - `thing_` dropped here while still borrowed
For more information about this error, try `rustc --explain E0597`.
error: could not compile `playground` due to previous error
How can I tell the rust compiler "&thing_ has the lifetime of 'struct1"?
Similar questions
Before this Question is marked as duplicate, these questions are similar but not quite the same. These Questions address 'static lifetimes or other slightly different scenarios.
Argument requires that value is borrowed for 'static not working for non copy value
Argument requires that _ is borrowed for 'static - how do I work round this?
"argument requires that record is borrowed for 'static" when using belonging_to associations with tokio-diesel
(tokio::spawn) borrowed value does not live long enough -- argument requires that sleepy is borrowed for 'static
Rust lifetime syntax when borrowing variables
Borrow data out of a mutex "borrowed value does not live long enough"
Rust: argument requires that borrow lasts for 'static
thing_ is passed by value. Its lifetime ends at the end of things_update. That means, the reference to thing_ becomes invalid after the end of the function but still be held by the struct, which is certainly should be rejected.
Like many times, actually the compiler is right and you were wrong. The compiler prevented you from use-after-free.
thing_ doesn't live long enough: you pushed a copy of it into the vector. It was clear if you'd used a type that isn't Copy:
type Thing<'a> = (&'a str, String);
// ...
Besides the previous error, now you also get:
error[E0382]: borrow of moved value: `thing_`
--> src/main.rs:24:35
|
21 | fn things_update(&mut self, thing_: Thing<'struct1>) {
| ------ move occurs because `thing_` has type `(&str, String)`, which does not implement the `Copy` trait
22 | self.things1.push(thing_);
| ------ value moved here
23 | let counter = self.thingscounter1.entry(
24 | ThingsCounterKey::new(&thing_)
| ^^^^^^^ value borrowed here after move
Playground.
In other case, you would have to first push the item into the vector and then retrieve a reference to the pushed item there, like:
self.things1.push(thing_);
let counter = self.thingscounter1.entry(
ThingsCounterKey::new(self.things1.last().unwrap())
).or_insert(0);
But in this case it will not work, and you'll get a long "cannot infer an appropriate lifetime for lifetime parameter in function call due to conflicting requirements" error. This is because you're essentially trying to do the impossible: create a self-referential struct. See Why can't I store a value and a reference to that value in the same struct? for more about this problem and how to solve it.

Why is "the temporary is part of an expression at the end of a block" an error?

This is likely a textbook case of me not understanding some of the technicalities of the borrow checker, but it would be nice if someone could clear this up for me.
I have this (incredibly simplified) chunk of code, which compiles perfectly fine.
pub struct Example(pub Vec<String>);
impl Example {
pub fn iter(&self) -> impl Iterator<Item=&String> {
self.0.iter()
}
}
pub fn some_condition(_: &str) -> bool {
// This is not important.
return false;
}
pub fn foo() -> bool {
let example = Example(vec!("foo".to_owned(), "bar".to_owned()));
let mut tmp = example.iter();
tmp.all(|x| some_condition(x))
}
pub fn main() {
println!("{}", foo());
}
However, the first thing that I tried (which, in my mind, should be equivalent to the above), was eliding the temporary variable tmp altogether, as follows
pub fn foo() -> bool {
let example = Example(vec!("foo".to_owned(), "bar".to_owned()));
example.iter().all(|x| some_condition(x))
}
But this version produces the following error.
error[E0597]: `example` does not live long enough
--> so_temporary.rs:23:3
|
23 | example.iter().all(|x| some_condition(x))
| ^^^^^^^-------
| |
| borrowed value does not live long enough
| a temporary with access to the borrow is created here ...
24 | }
| -
| |
| `example` dropped here while still borrowed
| ... and the borrow might be used here, when that temporary is dropped and runs the destructor for type `impl std::iter::Iterator`
|
= note: The temporary is part of an expression at the end of a block. Consider forcing this temporary to be dropped sooner, before the block's local variables are dropped. For example, you could save the expression's value in a new local variable `x` and then make `x` be the expression at the end of the block.
Now, obviously, the note at the end of the error is an excellent suggestion, and it's why I introduced the temporary to fix the problem. But I don't understand why that fixes the problem. What's different about the lifetimes of my tmp variable versus example.iter() embedded into the expression directly, that makes one work and one fail?
This has essentially the same answer as Why do I get "does not live long enough" in a return value? and its somewhat explained in the error itself, but I'll elaborate. This behavior is the same with a normal block expression:
pub struct Example(pub Vec<String>);
impl Example {
pub fn iter(&self) -> impl Iterator<Item=&String> {
self.0.iter()
}
}
pub fn main() {
let foo = {
let example = Example(vec!("foo".to_owned(), "".to_owned()));
example.iter().all(String::is_empty)
};
println!("{}", foo);
}
error[E0597]: `example` does not live long enough
--> src/main.rs:12:9
|
12 | example.iter().all(String::is_empty)
| ^^^^^^^-------
| |
| borrowed value does not live long enough
| a temporary with access to the borrow is created here ...
13 | };
| -- ... and the borrow might be used here, when that temporary is dropped and runs the destructor for type `impl Iterator`
| |
| `example` dropped here while still borrowed
|
= note: the temporary is part of an expression at the end of a block;
consider forcing this temporary to be dropped sooner, before the block's local variables are dropped
help: for example, you could save the expression's value in a new local variable `x` and then make `x` be the expression at the end of the block
|
12 | let x = example.iter().all(String::is_empty); x
| ^^^^^^^ ^^^
The scope of temporary values is often the statement in which they were created. In the code above example is a variable and it is destroyed at the end of the block. However, example.iter() creates a temporary impl Iterator and its temporary scope is the full let foo = ... statement. So the steps when evaluating this are:
evaluate the result of example.iter().all(...)
drop example
assign result to foo
drop impl Iterator
You can probably see where this can go wrong. The reason introducing a variable works is because it forces any temporaries to be dropped sooner. The case is slightly different when talking about functions, but the effect is the same:
Temporaries that are created in the final expression of a function body are dropped after any named variables bound in the function body, as there is no smaller enclosing temporary scope.
Regarding the comments:
The reason it works when impl Iterator is replaced with std::slice::Iter<'_, i32> (in pretzelhammer's example) is because the drop checker knows that slice::Iter doesn't access example on drop whereas it has to assume that impl Iterator does.
The reason it works with fn my_all(mut self, ...) (in Peter Hall's example) is because all takes the iterator by reference but my_all takes it by value. The temporary impl Iterator is consumed and destroyed before the end of the expression.
From looking at various Rust issues relating to this, its clear that some would consider this a bug. Its definitely not obvious that { ...; EXPR } and { ...; let x = EXPR; x } could be different. However, since diagnostics and documentation have been added to reinforce and explain this behavior, I have to assume these temporary scoping rules allow for more reasonable code than not.

Cyclic reference of RefCell borrows in traversal

I'm learning Rust and tried coding a doubly-linked list. However, I'm stuck already at a typical iterative traversal implementation. I'm getting the impression that the borrow checker / drop checker is too strict and cannot infer the correct lifetime for the borrow when it crosses the function boundary from RefCell. I need to repeatedly set a variable binding (curr in this case) to the borrow of its current contents:
use std::cell::RefCell;
use std::rc::Rc;
pub struct LinkedList<T> {
head: Option<Rc<RefCell<LinkedNode<T>>>>,
// ...
}
struct LinkedNode<T> {
value: T,
next: Option<Rc<RefCell<LinkedNode<T>>>>,
// ...
}
impl<T> LinkedList<T> {
pub fn insert(&mut self, value: T, idx: usize) -> &mut LinkedList<T> {
// ... some logic ...
// This is the traversal that fails to compile.
let mut curr = self.head.as_ref().unwrap();
for _ in 1..idx {
curr = curr.borrow().next.as_ref().unwrap()
}
// I want to use curr here.
// ...
unimplemented!()
}
}
The compiler complains:
Without NLL
error[E0597]: borrowed value does not live long enough
--> src/lib.rs:22:20
|
22 | curr = curr.borrow().next.as_ref().unwrap()
| ^^^^^^^^^^^^^ temporary value does not live long enough
23 | }
| - temporary value dropped here while still borrowed
...
28 | }
| - temporary value needs to live until here
|
= note: consider using a `let` binding to increase its lifetime
With NLL
error[E0716]: temporary value dropped while borrowed
--> src/lib.rs:22:20
|
22 | curr = curr.borrow().next.as_ref().unwrap()
| ^^^^^^^^^^^^^
| |
| creates a temporary which is freed while still in use
| a temporary with access to the borrow is created here ...
23 | }
| -
| |
| temporary value is freed at the end of this statement
| ... and the borrow might be used here, when that temporary is dropped and runs the destructor for type `std::cell::Ref<'_, LinkedNode<T>>`
|
= note: consider using a `let` binding to create a longer lived value
= note: The temporary is part of an expression at the end of a block. Consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped.
I would really appreciate a iterative solution (non-recursive) to this problem.
You can clone Rc to avoid lifetime issues:
let mut curr = self.head.as_ref().unwrap().clone();
for _ in 1..idx {
let t = curr.borrow().next.as_ref().unwrap().clone();
curr = t;
}
Here's a smaller reproduction that I believe shows the same problem:
use std::cell::RefCell;
fn main() {
let foo = RefCell::new(Some(42));
let x = foo.borrow().as_ref().unwrap();
}
As I read it:
foo.borrow() returns a cell::Ref, a type of smart pointer. In this case, the smart pointer acts like an &Option<i32>.
as_ref() creates an Option<&i32> where the inner reference has the same lifetime as the smart pointer.
The Option is discarded, yielding only an &i32, still with a lifetime of the smart pointer.
Notably, the smart pointer Ref only lasts for the statement, but the code attempts to return a reference into the Ref that would outlive the statement.
Generally, the solution would be to do something like this:
let foo_borrow = foo.borrow();
let x = foo_borrow.as_ref().unwrap();
This keeps the smart pointer around longer, allowing the lifetime of the reference to be valid for as long as foo_borrow (representing the borrow itself) exists.
In the case of a loop, there's not much you can do, as you essentially want to borrow every previous node until you get to the next one.

When would an implementation want to take ownership of self in Rust?

I'm reading through the Rust documentation on lifetimes. I tried something like:
struct S {
x: i8,
}
impl S {
fn fun(self) {}
fn print(&self) {
println!("{}", self.x);
}
}
fn main() {
let s = S { x: 1 };
s.fun();
s.print();
}
I get the following error:
error[E0382]: borrow of moved value: `s`
--> src/main.rs:16:5
|
15 | s.fun();
| - value moved here
16 | s.print();
| ^ value borrowed here after move
|
= note: move occurs because `s` has type `S`, which does not implement the `Copy` trait
This is because the fun(self) method takes ownership of the s instance. This is solved by changing to fun(&self).
I can't see why you would ever want to have a method on an object take control of itself. I can think of only one example, a destructor method, but if you wanted to do dispose of the object then it would be taken care of by the owner of the object anyway (i.e. scope of main in this example).
Why is it possible to write a method that takes ownership of the struct? Is there ever any circumstance where you would want this?
The idiomatic way to refer to a method that "takes control" of self in the Rust standard library documentation is to say that it "consumes" it. If you search for this, you should find some examples:
Option::unwrap_or_default
A lot in the Iterator trait.
As to why: you can try rewriting Iterator::map — you would end up having a lifetime parameter wandering around that would quickly become unmanageable. Why? Because the Map iterator is based upon the previous one, so the borrow checker will enforce that you can only use one of the two at the same time.
Conversion from type A to type B commonly involves functions taking self by value. See the implementors of Into and From traits for concrete examples.

Resources