I am trying to do the Simple Linked List Exercism problem, but I am stuck implementing the pop() method. The errors I get are not very clear to me; I'm not quite used to Rust's way of thinking, I guess.
Also, because of the compiler suggestions, I added a bunch of as_ref() that I don't really understand.
I am interested in knowing why my way of doing things doesn't work more than the solution.
pub struct SimpleLinkedList<T> {
head: Option<Box<Node<T>>>,
}
struct Node<T> {
data: T,
next: Option<Box<Node<T>>>,
}
impl<T> SimpleLinkedList<T> {
pub fn new() -> Self {
unimplemented!()
}
pub fn len(&self) -> usize {
unimplemented!()
}
pub fn push(&mut self, _element: T) {
unimplemented!()
}
pub fn pop(&mut self) -> Option<T> {
// Recursive function to return the before last element of the list
fn get_before_last<'a, T>(
prev: &'a Box<Node<T>>,
current: &'a Box<Node<T>>,
) -> &'a Box<Node<T>> {
match ¤t.next {
Some(next_node) => get_before_last(¤t, &next_node),
None => &prev,
}
}
// Check if the head is None
match &self.head {
None => return None,
_ => (),
};
let before_last = &mut match &self.head {
// Beginning of the recursion
Some(node) => get_before_last(&node, node.next.as_ref().unwrap()),
None => self.head.as_ref().unwrap(),
};
let to_pop = before_last.next.as_ref();
before_last.next = None;
Some(to_pop.unwrap().data)
}
}
I get the following errors:
error[E0594]: cannot assign to `before_last.next` which is behind a `&` reference
--> src/lib.rs:48:9
|
48 | before_last.next = None;
| ^^^^^^^^^^^^^^^^ cannot assign
error[E0507]: cannot move out of borrowed content
--> src/lib.rs:50:14
|
50 | Some(to_pop.unwrap().data)
| ^^^^^^^^^^^^^^^^^^^^ cannot move out of borrowed content
In your code, before_last is not mutable. In fact it's a &mut &'a Box<Node>. For this reason you cannot assign anything to the node because it's a mutable reference to an immutable one.
The best suggestion I can give you is to rethink the implementation. Instead of pushing and popping to the end of the chain, you can do so on the front.
Create a new boxed node, remove the head and put it in the new node's next field. Then the new node becomes the head.
This way you have a LIFO list and you don't have to traverse the whole list to push & pop, so it's also more efficient. Pushing to the front also reduces the amount of code required.
My solution is available on Exercism.
Related
I'm new to rust and try to understand &mut ref variables and mutability. I started creating a simple link list with pop_back function.
pub fn pop_back(&mut self) -> Option<T> {
let mut head = &mut self.head;
while let Some(v) = head {
if v.next.is_none() {
break;
}
head = &mut v.next;
}
head.take().map(|node| node.data)
}
but can't make it to work. error is cannot borrow *head as mutable more than once at a time.
How can I tell rust that I want to only change the reference in my loop not the value?
I don't want to add another tail variable to my list so without changing structure how can I make this work?
this is the struct definition
pub struct Node<T> {
data: T,
next: Option<Box<Node<T>>>
}
pub struct SimpleLinkedList<T> {
head: Option<Box<Node<T>>>,
}
This is a known limitation of the borrow checker. The next-gen Polonius will solve this.
In the meantime, the solution (without unsafe) is to repeat the calculation. In your case, this means some unwrap()s:
pub fn pop_back(&mut self) -> Option<T> {
let mut head = &mut self.head;
while head.is_some() {
if head.as_mut().unwrap().next.is_none() {
break;
}
head = &mut head.as_mut().unwrap().next;
}
head.take().map(|node| node.data)
}
See also:
Cannot borrow as mutable in loop
Returning a reference from a HashMap or Vec causes a borrow to last beyond the scope it's in?
Stripped down to the bare essentials, my problematic code looks as follows:
pub struct Item;
impl Item {
/// Partial copy. Not the same as simple assignment.
pub fn copy_from(&mut self, _other: &Item) {
}
}
pub struct Container {
items: Vec<Item>,
}
impl Container {
pub fn copy_from(&mut self, self_idx: usize, other: &Container, other_idx: usize) {
self.items[self_idx].copy_from(&other.items[other_idx]);
}
}
fn main() {
let mut container = Container { items: vec![Item, Item] };
container.copy_from(0, &container, 1);
}
This is of course rejected by the borrow checker:
error[E0502]: cannot borrow `container` as mutable because it is also borrowed as immutable
--> src/main.rs:21:5
|
21 | container.copy_from(0, &container, 1);
| ^^^^^^^^^^---------^^^^----------^^^^
| | | |
| | | immutable borrow occurs here
| | immutable borrow later used by call
| mutable borrow occurs here
For more information about this error, try `rustc --explain E0502`.
I understand why that happens, but I don't have a good solution.
I've considered adding a dedicated copy_from_self function that callers need to use in cases where self == other:
pub fn copy_from_self(&mut self, to_idx: usize, from_idx: usize) {
if to_idx != from_idx {
unsafe {
let from_item: *const Item = &self.items[from_idx];
self.items[to_idx].copy_from(&*from_item);
}
}
}
But this is un-ergonomic, bloats the API surface, and needs unsafe code inside.
Note that in reality, the internal items data structure is not a simple Vec, so any approach specific to Vec or slice will not work.
Is there an elegant, idiomatic solution to this problem?
If I understand the comments on the question correctly, a general solution seems to be impossible, so this answer is necessarily specific to my actual situation.
As mentioned, the actual data structure is not a Vec. If it were a Vec, we could use split_at_mut to at least implement copy_from_self safely.
But as it happens, my actual data structure is backed by a Vec, so I was able to add a helper function:
/// Returns a pair of mutable references to different items. Useful if you need to pass
/// a reference to one item to a function that takes `&mut self` on another item.
/// Panics if `a == b`.
fn get_mut_2(&mut self, a: usize, b: usize) -> (&mut T, &mut T) {
assert!(a != b);
if a < b {
let (first, second) = self.items.split_at_mut(b);
(&mut first[a], &mut second[0])
} else if a > b {
let (first, second) = self.items.split_at_mut(a);
(&mut second[0], &mut first[b])
} else {
panic!("cannot call get_mut_2 with the same index {} == {}", a, b);
}
}
Now we can implement copy_from_self without unsafe code:
pub fn copy_from_self(&mut self, to_idx: usize, from_idx: usize) {
let (to, from) = self.items.get_mut_2(to_idx, from_idx);
to.unwrap().copy_from(from.unwrap());
}
Being new to rust I wanted to play with some data structures and ended up with something like a node type without payload.
use std::cell::RefCell;
use std::collections::HashMap;
use std::ops::Drop;
#[derive(Debug)]
struct Container<'a> {
next : Option<&'a RefCell<Container<'a>>>,
}
impl<'a> Container<'a> {
fn new() -> Container<'a> {
Container { next: None }
}
fn set(&mut self, next: &'a RefCell<Container<'a>>) {
self.next = Some(next);
}
}
The goal was to have these nodes not own their neighbours, so std::rc::Rc was out of the question.
So I did some testing which went fine:
fn main() {
// items:
let cont_1 = RefCell::new(Container::new());
let cont_2 = RefCell::new(Container::new());
let b_1 = &cont_1;
let b_2 = &cont_2;
(*b_2).borrow_mut().set(b_1);
(*b_1).borrow_mut().set(b_2);
println!("{:?}", b_1.borrow());
}
Since I was playing around I then tried to implement the Drop trait on the Container type
impl<'a> Drop for Container<'a>{
fn drop(&mut self) {}
}
which results in two of (the other one for cont_2)
error[E0597]: `cont_1` does not live long enough
--> src/main.rs:11:15
|
11 | let b_1 = &cont_1;
| ^^^^^^^ borrowed value does not live long enough
...
18 | }
| -
| |
| `cont_1` dropped here while still borrowed
| borrow might be used here, when `cont_1` is dropped and runs the destructor for type `std::cell::RefCell<Container<'_>>`
Now, I believe, that Drop causes the deallocation to be at the end of scopes otherwise it would usually take place after the last use? But either way the complaint is about the value not living long enough... I have tried adding drop(...) in between, but failed. I guess I dont even understand how exactly the order of deallocation changed. I would expect that cont_1 would be deallocated last since it was initialized/declared first meaning that I don't really understand how it could still be borrowed.
What would happen if in your drop() implementation you use self.next.unwrap()...? Since one of your variables will necessarily be dropped before the other, the last one will have a dangling reference, and so undefined behavior. So you code is correct in not to compile.
IMO, the solution is to use some kind of reference counted pointers. If you do not want Rc, because they do not own the neighbors (it will create a reference loop and thus leak your objects), you can use Weak references. Something like this (playground):
use std::cell::RefCell;
use std::ops::Drop;
use std::rc::{Rc, Weak};
#[derive(Debug)]
struct Container {
next : Option<Weak<RefCell<Container>>>,
}
impl Container {
fn new() -> Container {
Container { next: None }
}
fn set(&mut self, next: &Rc<RefCell<Container>>) {
self.next = Some(Rc::downgrade(next));
}
}
impl Drop for Container{
fn drop(&mut self) {}
}
fn main() {
// items:
let cont_1 = Rc::new(RefCell::new(Container::new()));
let cont_2 = Rc::new(RefCell::new(Container::new()));
cont_1.borrow_mut().set(&cont_1);
cont_2.borrow_mut().set(&cont_2);
println!("{:?}", cont_1.borrow());
}
I have tried the examples in Learn Rust with Entirely Too Many LInked Lists, but I would like to make a linked list structure without a dedicated struct to point to the head of the list.
In the pop() function, since I only have a mutable reference to the node, so I "cannot move out of borrowed content". Is there a way to take out the node with only mutable reference or ownership is the requirement?
I have tried to use mem::replace(), but it's not for removing elements. Also the replacement on initializing a new node is pretty weird. Is there a more elegant way of writing a linked list without head pointer?
use std::mem;
type Link<T> = Option<Box<Node<T>>>;
pub struct Node<T> {
val: T,
next: Link<T>,
}
impl<T:Copy> Node<T> {
fn new(val: T) -> Self {
Node {
val: val,
next: None,
}
}
fn push(&mut self, val: T) {
let mut new_node = Node {
val: val,
next: Some(Box::new(mem::replace(self, Node::new(val)))),
};
self = &mut new_node;
}
pub fn pop(mut self) -> Option<T> {
let val = self.val;
self = *self.next.unwrap();
Some(val)
}
}
I tried to implement own analogue of find_or_insert method that looks like this:
use std::collections::HashMap;
pub struct SomeManager {
next: i32,
types: HashMap<i32, i32>,
}
impl SomeManager {
pub fn get_type<'a>(&'a mut self, k: i32) -> &'a i32 {
match self.types.get(&k) {
Some(ref x) => return *x,
None => {
self.types.insert(k, self.next);
self.next += 1;
return self.types.get(&k).unwrap();
}
}
}
}
fn main() {}
Error:
error[E0502]: cannot borrow `self.types` as mutable because it is also borrowed as immutable
--> src/main.rs:13:17
|
10 | match self.types.get(&k) {
| ---------- immutable borrow occurs here
...
13 | self.types.insert(k, self.next);
| ^^^^^^^^^^ mutable borrow occurs here
...
18 | }
| - immutable borrow ends here
I know that there are some standard methods that implement this functionality, but I want this method to be as light as possible - it will be called very-very often and almost all of the time the values will already exist.
As I understand it, when we call self.types.get we borrow it to scope of match statement, so we can't call self.types.insert here. I have tried to move methods from None branch out of match statement, but it also fails.
The only working solution that I found requires invoking get twice:
pub fn get_type<'a>(&'a mut self, k: i32) -> &'a i32 {
let is_none = match self.types.get(&k) {
Some(ref x) => false,
None => true,
};
if is_none {
self.types.insert(k, self.next);
self.next += 1;
}
self.types.get(&k).unwrap()
}
How can I work around such situations?
There are a handful of methods on HashMap to achieve these sorts of complex cases. Most notably, for your case, HashMap::entry and Entry::or_insert_with:
pub fn get_type<'a>(&'a mut self, k: i32) -> &'a i32 {
self.types.entry(k).or_insert_with(|| {
let value = self.next;
self.next += 1;
value
})
}
In your case, however, there’s the borrow of self inside, so that won’t do.
We thus shift the borrow of self.next outside of the closure so the compiler can reason about it as disjoint from self.types. Problem solved with only one lookup, as it should be.
pub fn get_type<'a>(&'a mut self, k: i32) -> &'a i32 {
let next = &mut self.next;
self.types.entry(k).or_insert_with(|| {
let value = *next;
*next += 1;
value
})
}
Note that in your first case you're doing one lookup when the key exists in the map and three when it does not exist. Your last attempt does two lookups in either case. This is somewhat prettified version of the latter:
pub fn get_type<'a>(&'a mut self, k: i32) -> &'a i32 {
let contains = self.types.contains_key(&k);
if !contains {
self.types.insert(k, self.next);
self.next += 1;
}
self.types.get(&k).unwrap()
}
I don't think it is possible to avoid the second lookup without some support from the map's implementation because of borrowing restrictions.
In any case, using the solution by Chris Morgan is superior to the above one (for example, it may be more efficient and in fact require less lookups), so I suggest sticking with it.