Reputation: 55
I've been experimenting with using more complicated structs and I'm having trouble when trying to edit values using one struct when the value is contained in another struct. The goal of this is to be able write a simple abstractions from potential users point of view so that they just have one struct that they have to mutate and play with.
Example Code:
#[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<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 = Widget::new();
let user = User::new(user_widget);
self.widgets.push(user_widget);
return user;
}
}
pub struct User {
name: String,
widget: Widget
}
impl User {
pub fn new(user_widget: Widget) -> User {
let user = User {
name: "User1".to_string(),
widget: user_widget
};
return user;
}
pub fn update_count(&mut self) {
self.widget.counter +=1;
}
}
pub fn main() {
let mut market = Market::new();
let mut user1 = market.new_user();
println!("{:?}", market.widgets);
user1.update_count();
println!("{:?}", market.widgets);
}
Example Output:
Compiling playground v0.0.1 (/playground)
error[E0382]: use of moved value: `user_widget`
--> src/main.rs:31:27
|
29 | let user_widget = Widget::new();
| ----------- move occurs because `user_widget` has type `Widget`, which does not implement the `Copy` trait
30 | let user = User::new(user_widget);
| ----------- value moved here
31 | self.widgets.push(user_widget);
| ^^^^^^^^^^^ value used here after move
For more information about this error, try `rustc --explain E0382`.
error: could not compile `playground` due to previous error
In theory I would want the widget in user to be a reference to the widget but I would be unable to initialize user with a reference and then modify that reference. I've looked into trying to use Arc<T>
or RC<T>
but I'm unsure if I need to wrap both my vector that stores the widgets with these types and the user that references it. Can I just use it once in the User struct?
Upvotes: 3
Views: 2474
Reputation: 2561
Using Rc
and Mutex
, you can wrap each of your Widget
s so that you pass around a mutable reference to that individual struct.
I modified your code a bit too so it was easier for me to work in, I hope is still plenty readable:
use std::rc::Rc;
use std::sync::Mutex;
#[derive(Debug)]
pub struct Widget {
counter: u16,
}
impl Widget {
pub fn new() -> Widget {
Widget { counter: 0 }
}
}
type MutWidget = Rc<Mutex<Widget>>;
pub struct Market {
widgets: Vec<MutWidget>,
}
impl Market {
pub fn new() -> Market {
Market {
widgets: Vec::new(),
}
}
pub fn new_user(&mut self) -> User {
let user_widget = Rc::new(Mutex::new(Widget::new()));
let user = User::new(user_widget.clone());
self.widgets.push(user_widget.clone());
return user;
}
}
pub struct User {
name: String,
widget: MutWidget,
}
impl User {
pub fn new(user_widget: MutWidget) -> User {
User {
name: "User1".to_string(),
widget: user_widget,
}
}
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);
}
Upvotes: -1
Reputation: 22396
You are actually modifying the values through all these instances, which makes the problem a bit harder.
The basic of ownership in rust says three things:
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.
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 } }]
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 } }]
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 }]
Upvotes: 6