Hi. I'm new at Rust and follow the docs to learn about it. Recently I read about ownership and borrowing and when I tried to implement a simple logic I had some problems. I hope someone helps me to understand what's going on. This is my code:
I want to add each user to following and follower list without using copy or clone. Is it possible? the main problem that I had was using mutable reference twice or using immutable and mutable reference at a time. How can I implement this logic?
I'm a bit short on time, so can't put together a good explanation. Working with circular references like you have is tricky. It won't work at all with &mut, which requires there to be no other references to the same object.
You can do this with some interior mutability, though:
use std::cell::RefCell;
struct User<'a> {
username: String,
following: RefCell<Vec<&'a User<'a>>>,
follower: RefCell<Vec<&'a User<'a>>>,
}
impl<'a> User<'a> {
fn new(username: String) -> Self {
User {
username,
following: RefCell::new(vec![]),
follower: RefCell::new(vec![]),
}
}
fn send_post(&self, post: &str) {
for user in self.follower.borrow().iter() {
// do somtihng
}
}
fn follow(&'a self, user: &'a User<'a>) {
self.following.borrow_mut().push(user);
user.follower.borrow_mut().push(self);
}
}
fn main() {
let mut user1 = User::new("user1".to_string());
let mut user2 = User::new("user2".to_string());
user1.follow(&user2);
user2.follow(&user1);
}
You should use Arc<Mutex<User>>, which is an equivalent of what User would be in Java/Python/JS and other GC languages.
Anything with &'a will make you stuck and hate Rust. Don't put references in structs until you have at least a year experience with Rust. I'm serious. It's an advanced feature that doesn't do what new users think it does.
& is not a general-purpose reference to another object. It is specifically a temporary borrow of an object, limited to a scope, and forces its target to be immutable.
& implies these objects are stored elsewhere, and the web of temporary mutual borrows you make will force them all to be immutable, and impossible to free/delete other than all of them at once (e.g. you'd have to use a memory pool/arena that outlives the entire graph).
OTOH if you use type that isn't a temporary view, you will be able to store User objects wherever you want, create and delete them, pass them around freely.
follow takes a &'a mut User<'a> (notice the 'a in both the reference and the type), which is never what you want. This will require you to have a reference that exists for at least as long as the pointed data do, thus locking your struct in place forever.
Also be careful with that &'a User<'a>, depending on how User is defined it could bring the same problems as &'a mut User<'a>.
When learning rust you should be careful when describing something as "simple logic". It may work easily in your mind, however you'll often find that it lacks clear ownership semantics.
Fwiw, here's how I might approach this using a "poor man's arena" approach, keeping all the users in a Vec and passing around indices instead of references: playground. Happy to explain the design a bit more if something about it is confusing.