You are actually modifying the values through all these instances, which makes the problem a bit harder.
Background
The basic of ownership in Rust says three things:
- Every object is owned by exactly one thing
- Objects can be read by multiple immutable references
- Object can be written to only by one mutable reference, and if a mutable reference exist, there cannot be any other references (including immutable ones).
This also applies to Rc and Arc, meaning, while they give access to multiple 'owners', they only do so immutably.
To actually then modify the values, you need to create interior mutability. This is usually done with RefCell in the single threaded case, or with Mutex in the multi threaded case.
Solution #1
Here is your code with Rc<RefCell>:
use std::{cell::RefCell, rc::Rc};
#[derive(Debug)]
pub struct Widget {
counter: u16,
}
impl Widget {
pub fn new() -> Widget {
let nw = Widget { counter: 0 };
return nw;
}
}
pub struct Market {
widgets: Vec<Rc<RefCell<Widget>>>,
}
impl Market {
pub fn new() -> Market {
let market_vec = Vec::new();
let market = Market {
widgets: market_vec,
};
return market;
}
pub fn new_user(&mut self) -> User {
let user_widget = Rc::new(RefCell::new(Widget::new()));
let user = User::new(user_widget.clone());
self.widgets.push(user_widget);
return user;
}
}
pub struct User {
name: String,
widget: Rc<RefCell<Widget>>,
}
impl User {
pub fn new(user_widget: Rc<RefCell<Widget>>) -> User {
let user = User {
name: "User1".to_string(),
widget: user_widget,
};
return user;
}
pub fn update_count(&mut self) {
self.widget.borrow_mut().counter += 1;
}
}
pub fn main() {
let mut market = Market::new();
println!("{:?}", market.widgets);
let mut user1 = market.new_user();
user1.update_count();
println!("{:?}", market.widgets);
}
[]
[RefCell { value: Widget { counter: 1 } }]
Solution #2
In your specific case, I noticed that the only thing you actually update is the counter.
Therefore, you wouldn't actually need to make the entire Widget mutable, but instead, you could make only the counter mutable. The counter is simpler then the Widget class, therefore we can optimize it a little.
In the single threaded case, we can use Cell. Cell is the same as RefCell, but cannot fail. But Cell only exists for copyable objects.
In the multi-threaded case, we can use AtomicU16. It is hugely more efficient than a Mutex; actually, it has zero overhead compared to a normal u16 in most cases.
Here is the solution with Cell<u16>:
use std::{cell::Cell, rc::Rc};
#[derive(Debug)]
pub struct Widget {
counter: Cell<u16>,
}
impl Widget {
pub fn new() -> Widget {
let nw = Widget { counter: 0.into() };
return nw;
}
}
pub struct Market {
widgets: Vec<Rc<Widget>>,
}
impl Market {
pub fn new() -> Market {
let market_vec = Vec::new();
let market = Market {
widgets: market_vec,
};
return market;
}
pub fn new_user(&mut self) -> User {
let user_widget = Rc::new(Widget::new());
let user = User::new(user_widget.clone());
self.widgets.push(user_widget);
return user;
}
}
pub struct User {
name: String,
widget: Rc<Widget>,
}
impl User {
pub fn new(user_widget: Rc<Widget>) -> User {
let user = User {
name: "User1".to_string(),
widget: user_widget,
};
return user;
}
pub fn update_count(&mut self) {
let prev = self.widget.counter.get();
self.widget.counter.set(prev + 1);
}
}
pub fn main() {
let mut market = Market::new();
println!("{:?}", market.widgets);
let mut user1 = market.new_user();
user1.update_count();
println!("{:?}", market.widgets);
}
[]
[Widget { counter: Cell { value: 1 } }]
Thread safe versions
For completeness sake, here are the same solutions in a multi-threaded context.
With Arc<Mutex>:
use std::sync::{Arc, Mutex};
#[derive(Debug)]
pub struct Widget {
counter: u16,
}
impl Widget {
pub fn new() -> Widget {
let nw = Widget { counter: 0 };
return nw;
}
}
pub struct Market {
widgets: Vec<Arc<Mutex<Widget>>>,
}
impl Market {
pub fn new() -> Market {
let market_vec = Vec::new();
let market = Market {
widgets: market_vec,
};
return market;
}
pub fn new_user(&mut self) -> User {
let user_widget = Arc::new(Mutex::new(Widget::new()));
let user = User::new(user_widget.clone());
self.widgets.push(user_widget);
return user;
}
}
pub struct User {
name: String,
widget: Arc<Mutex<Widget>>,
}
impl User {
pub fn new(user_widget: Arc<Mutex<Widget>>) -> User {
let user = User {
name: "User1".to_string(),
widget: user_widget,
};
return user;
}
pub fn update_count(&mut self) {
self.widget.lock().unwrap().counter += 1;
}
}
pub fn main() {
let mut market = Market::new();
println!("{:?}", market.widgets);
let mut user1 = market.new_user();
user1.update_count();
println!("{:?}", market.widgets);
}
[]
[Mutex { data: Widget { counter: 1 }, poisoned: false, .. }]
With AtomicU16:
use std::{
sync::atomic::{AtomicU16, Ordering},
sync::Arc,
};
#[derive(Debug)]
pub struct Widget {
counter: AtomicU16,
}
impl Widget {
pub fn new() -> Widget {
let nw = Widget { counter: 0.into() };
return nw;
}
}
pub struct Market {
widgets: Vec<Arc<Widget>>,
}
impl Market {
pub fn new() -> Market {
let market_vec = Vec::new();
let market = Market {
widgets: market_vec,
};
return market;
}
pub fn new_user(&mut self) -> User {
let user_widget = Arc::new(Widget::new());
let user = User::new(user_widget.clone());
self.widgets.push(user_widget);
return user;
}
}
pub struct User {
name: String,
widget: Arc<Widget>,
}
impl User {
pub fn new(user_widget: Arc<Widget>) -> User {
let user = User {
name: "User1".to_string(),
widget: user_widget,
};
return user;
}
pub fn update_count(&mut self) {
self.widget.counter.fetch_add(1, Ordering::SeqCst);
}
}
pub fn main() {
let mut market = Market::new();
println!("{:?}", market.widgets);
let mut user1 = market.new_user();
user1.update_count();
println!("{:?}", market.widgets);
}
[]
[Widget { counter: 1 }]
Rcis definitely the way to go (Arcis only necessary if you are using threads). You probably want to take a look atMutexto enable mutability between your references.