Since recently, I started learning Rust and I’m a bit confused with the borrowing and lifetimes concepts. For practice I decided to implement a simple linked list. This is my initial code:
use std::fmt::Display;
#[derive(Debug)]
struct Elem<T> {
val: T,
next: Option<Box<Elem<T>>>
}
impl<T: Display> Elem<T> {
fn print(&self) {
print!("{}, ", self.val);
match &self.next {
Some(x) => {
x.as_ref().print();
},
_ => {}
}
}
}
#[derive(Debug)]
struct MyList<T> {
first_element: Option<Elem<T>>
}
impl<T> MyList<T> {
fn new() -> MyList<T> {
MyList {
first_element: None
}
}
fn add(&mut self, x: T) {
match &self.first_element {
None => {
self.first_element = Some(Elem {
val: x,
next: None
});
},
Some(_) => {
let tmp = self.first_element.take();
self.first_element = Some(Elem {
val: x,
next: Some(Box::new(tmp.unwrap()))
});
}
}
}
}
impl <T: Display> MyList<T> {
fn print(&self) {
print!("{}", "[");
match &self.first_element {
None => {},
Some(e) => {
e.print();
}
}
println!("{}", "]");
}
}
fn main() {
let mut l = MyList::new();
l.add(1);
l.add(2);
l.add(3);
l.print();
}
And it worked perfectly. Next, I decided to a mutable reference to the last element of the list, just to be able to easily append new elements at the back. So, my code changed to this:
use std::fmt::Display;
#[derive(Debug)]
struct Elem<T> {
val: T,
next: Option<Box<Elem<T>>>
}
impl<T: Display> Elem<T> {
fn print(&self) {
print!("{}, ", self.val);
match &self.next {
Some(x) => {
x.as_ref().print();
},
_ => {}
}
}
}
#[derive(Debug)]
struct MyList<'a, T> {
first_element: Option<Elem<T>>,
last_element: Option<&'a mut Elem<T>>
}
impl<'a, T> MyList<'a, T> {
fn new() -> MyList<'a, T> {
MyList {
first_element: None,
last_element: None
}
}
fn add(&'a mut self, x: T) {
match &self.first_element {
None => {
self.first_element = Some(Elem {
val: x,
next: None
});
self.last_element = Some(self.first_element.as_mut().unwrap());
},
Some(_) => {
let tmp = self.first_element.take();
self.first_element = Some(Elem {
val: x,
next: Some(Box::new(tmp.unwrap()))
});
}
}
}
}
impl <'a, T: Display> MyList<'a, T> {
fn print(&self) {
print!("{}", "[");
match &self.first_element {
None => {},
Some(e) => {
e.print();
}
}
println!("{}", "]");
}
}
fn main() {
let mut l = MyList::new();
l.add(1);
l.add(2);
l.add(3);
l.print();
}
The change is, that I had to add a lifetime parameter to the struct, because of the reference property. However, in my add function I was forced to take self as a mutable reference with a lifetime parameter ‘a, in order to be able to properly assign the last_element property. As far I’m understanding, once called the function add creates a mutable reference to the MyList struct, which will live as long as the struct lives itself. This however, makes any other calls of the add function impossible.
Is there some way to get rid of the lifetime parameter and be able to call my add function more than once? Or what’s a better way to implement the whole thing in Rust? Thanks in advance!