Hello,
I'm new to Rust and have a question about traits and Rc. Initially
ui_widgets was a Vec<Box<dyn UIElement>>
, than i needed to change the position of my
robot widget. I switched than to Rc<RefCell<...>>
that I can modify the robot widget. If this approach the right one or can this solved in another way?
Thanks, BR
Martin
let grid_map = GridMap::new(100, 100, map);
let mut grid_map_widget = GridMapWidget::new(&grid_map, 5.0, 5.0);
grid_map_widget.show_grid = true;
let mut robot_widget = Rc::new(RefCell::new(RobotWidget::new(
200.0, 200.0,
0.0)));
let ui_widgets : Vec<Rc<RefCell<dyn UIElement>>> = vec![Rc::new(RefCell::new(grid_map_widget)),
robot_widget.clone()];
let mut speed = 0f32;
let dt = 1.0;
let mut last_time_stamp = get_time();
loop {
clear_background(BLACK);
if get_time() - last_time_stamp > 0.05 {
last_time_stamp = get_time();
let mut r = robot_widget.borrow_mut();
if is_key_down(KeyCode::Up) {
speed += 0.1;
} else if is_key_down(KeyCode::Down) {
speed -= 0.1;
} else if is_key_down(KeyCode::Left) {
r.angle -= 0.01;
}else if is_key_down(KeyCode::Right){
r.angle += 0.01;
}
let delta_x = speed * dt * r.angle.cos();
let delta_y = speed * dt * r.angle.sin();
r.x += delta_x;
r.y += delta_y;
}
for element in &ui_widgets {
let borrowed = element.borrow();
borrowed.render();
}
next_frame().await;
}
there's not enough context to draw a definite conclusion, for example, what GUI library are you using, how the UIElement
trait is defined?. but just by the example code, you don't need Rc<RefCell<...>>
: there's no shared mutability in the code anywhere that I can see.
did you get a compile error when using Vec<Box<dyn UiElement>>
? what is the error message exactly?
Thanks for your response.
I needed to modifiy the position of the robot, which is represented by the Robotwidget.
When I store it as Box<dyn UIElement>
, what I've know so far there is now way to get the original type back or?
To tackle is I decided to switch to Rc(Vec<Rc<dyn UIElement>>
). The position and angle of the robot widget are updated when there are keypresses occuring. When I try to modify than the robot widget I get following from the compiler:
help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Rc<RobotWidget>`
error[E0594]: cannot assign to data in an `Rc`
That's how I ended up with Vec<Rc<RefCell<dyn UIElement>>>
Why do you need Rc and dyn traits at all?
Here is a simple code without virtual calls (robots are owned by vectors).
fn main() {
let mut robots = vec![Robot::default()];
let mut ships = vec![SpaceShip::default()];
render(robots.as_slice());
render(ships.as_slice());
}
fn render<T: Widget>(widgets: &[T]) {
for w in widgets {
w.render();
}
}
The only problem here is render
created two times: for robots and ships.
We can use virtual calls. We need to:
- store items behind RC (robots are now owned by rc)
- create another vector of references to the same objects
fn main() {
let mut robots: Vec<Rc<Robot>> = vec![];
let mut ships: Vec<Rc<SpaceShip>> = vec![];
let mut ui_widgets: Vec<&dyn Widget> = vec![];
// add robot
let robot = Rc::new(Robot::default());
ui_widgets.push(robot.as_ref());
robots.push(robot.clone());
//render
render(&ui_widgets);
}
fn render(widgets: &[&dyn Widget]) {
for &w in widgets {
w.render();
}
}
There are, of course, lots of solutuions between
fn main() {
let mut robots: Vec<Robot> = vec![];
let mut ships: Vec<SpaceShip> = vec![];
// add robot
let robot = Robot::default();
robots.push(robot);
//render
for r in robots {
render(&r)
}
for s in ships {
render(&s)
}
}
fn render(widget: &dyn Widget) {
// Some heavy logic here
widget.render();
}
Dynamic traits and virtual dispatch aren't free, so I prefer the first one (unless I have lots of polymorphic code of course)
2 Likes
Why do you need Rc and dyn traits at all?
The idea was to store things, which need to be visualized, into a vector and call simply render of them. This will be the grid map, the robot and in future other things. Since the elements in the vector not the same type, I can't use generics with trait bounds. Thats the reason why I went for virtual dispatch.
The idea of this application is for simulation various robotic algorithms and visualize them.
During the loop the robot position will get updated and therefore the widget needs to be updated where the robot now is. I need the robot widget to set the actual position. That's the reason why I choose the Rc, it can live in the vector and somewhere else. That lead me to next question, I need than use RefCell
when I wan't to modify this?
Quote from rc module
Shared references in Rust disallow mutation by default, and Rc
is no exception: you cannot generally obtain a mutable reference to something inside an Rc
. If you need mutability, put a Cell
or RefCell
inside the Rc
; see [an example of mutability inside an Rc
I'm open for other ideas or solutions.
For example here the grid map and the robot visualized:
I think you could combine Rc
with RefCell
: former is for sharing and later is for interior mutability i.e:
use std::cell::RefCell;
use std::rc::Rc;
struct Robot {
pub x: u8,
pub y: u8,
}
struct Human {}
trait Widget {
fn render(&self);
}
impl Widget for Robot {
fn render(&self) {
println!("I am a robot at {}, {}", self.x, self.y);
}
}
impl Widget for Human {
fn render(&self) {
println!("i am a human");
}
}
fn main() {
// Just widgets.
let mut ui_widgets: Vec<Rc<RefCell<dyn Widget>>> = vec![];
let mut robots = vec![];
let mut humans = vec![];
for y in 1..10 {
let robot = Rc::new(RefCell::new(Robot { x: 100, y }));
let human = Rc::new(RefCell::new(Human {}));
ui_widgets.push(robot.clone());
ui_widgets.push(human.clone());
robots.push(robot);
humans.push(human);
}
// Mutate
robots[0].borrow_mut().x = 1;
render(ui_widgets.as_slice());
}
// I render widgets and know nothing about their real types
fn render(widgets: &[Rc<RefCell<dyn Widget>>]) {
for x in widgets {
x.borrow().render();
}
}
So each object has two references: one in its own vector and other one in widgets
.
(which is very close to your solution)
PS: But I still think that separate widgets
collection is a bit redundant here.
If we were able to get rid of it, we wouldn't need Rc+RefCell+vtable
fn main() {
// Just widgets.
let mut robots = vec![];
let mut humans = vec![];
for y in 1..10 {
robots.push(Robot { x: 1, y });
humans.push(Human {});
}
// Mutate
robots[0].y += 1;
render(robots.as_slice());
}
// I render widgets and know nothing about their real types
fn render<T: Widget>(widgets: &[T]) {
for w in widgets {
w.render();
}
}