I've been playing with Rust for the past few days,
and I'm still struggling with the memory management (figures).
I wrote a simple project that creates a hierarchy of structs ("keywords") from lexing/parsing a text file.
A keyword is defined like this:
pub struct Keyword<'a> {
name: String,
code: u32,
parent: Option<&'a Keyword<'a>>,
}
which is my equivalent for this C struct:
typedef struct Keyword Keyword;
struct Keyword {
char* name;
unsigned int code;
Keyword* parent;
};
A hierarchy is just a container for keywords, defined like this:
pub struct KeywordHierarchy<'a> {
keywords: Vec<Box<Keyword<'a>>>,
}
impl<'a> KeywordHierarchy<'a> {
fn add_keyword(&mut self, keyword: Box<Keyword<'a>>) {
self.keywords.push(keyword);
}
}
In the parse function (which is a stub of the complete parser), I recreated the condition that spawns the lifetime error in my code.
fn parse<'a>() -> Result<KeywordHierarchy<'a>, String> {
let mut hierarchy = KeywordHierarchy { keywords: Vec::new() };
let mut first_if = true;
let mut second_if = false;
while true {
if first_if {
// All good here.
let root_keyword = Keyword {
name: String::from("ROOT"),
code: 0,
parent: None,
};
hierarchy.add_keyword(Box::new(root_keyword));
first_if = false;
second_if = true;
}
if second_if {
// Hierarchy might have expired here?
// Find parent
match hierarchy.keywords.iter().find(|p| p.code == 0) {
None => return Err(String::from("No such parent")),
Some(parent) => {
// If parent is found, create a child
let child = Keyword {
name: String::from("CHILD"),
code: 1,
parent: Some(&parent),
};
hierarchy.add_keyword(Box::new(child));
}
}
second_if = false;
}
if !first_if && !second_if {
break;
}
}
Ok(hierarchy)
}
There's a while loop that goes through the lexer tokens.
In the first if, I add the ROOT keyword to the hierarchy, which is the only one that doesn't have a parent, and everything goes smoothly as expected.
In the second if, I parse the children keywords and I get a lifetime error when invoking KeywordHierarchy.add_keyword().
hierarchy.keywords` does not live long enough
Could you guys recommend an idiomatic way to fix this?
Cheers.
P.S. Click here for the playground
The immediate problem I can see with your design is that your loop is going to modify the hierarchy.keywords vector (in the first_if block), but it also borrows elements from the hierarchy.keywords vector (in the second_if block).
This is problematic, because modifying a vector may cause its backing buffer to be reallocated, which, if it were allowed, would invalidate all existing borrows to the vector. (And thus it is not allowed.)
Have you considered using an arena to hold your keywords instead of a Vec? Arenas are designed so that you can allocate new things within them while still having outstanding borrows to elements previously allocated within the arena.
Update: Here is a revised version of your code that illustrates using an arena (in this case a rustc_arena::TypedArena, but that's just so I can get this running on the play.rust-lang.org service) alongside a Vec<&Keyword> to handle the lookups.
https://play.rust-lang.org/?gist=fc6d81cb7efa7e4f32c481ab6482e587&version=nightly&backtrace=0
The crucial bits of code are this:
First: the KeywordHierarchy now holds a arena alongside a vec:
pub struct KeywordHierarchy<'a> {
keywords: Vec<&'a Keyword<'a>>,
kw_arena: &'a TypedArena<Keyword<'a>>,
}
Second: Adding a keyword now allocates the spot in the arena, and stashes a reference to that spot in the vec:
fn add_keyword(&mut self, keyword: Keyword<'a>) {
let kw = self.kw_arena.alloc(keyword);
self.keywords.push(kw);
}
Third: the fn parse function now takes an arena (reference) as input, because we need the arena to outlive the stack frame of fn parse:
fn parse<'a>(arena: &'a TypedArena<Keyword<'a>>) -> Result<KeywordHierarchy<'a>, String> {
...
Fourth: To avoid borrow-checker issues with borrowing hierarchy as mutable while also iterating over it, I moved the hierarchy modification outside of your Find parent match:
// Find parent
let parent = match hierarchy.keywords.iter().find(|p| p.code == 0) {
None => return Err(String::from("No such parent")),
Some(parent) => *parent, // (this deref is important)
};
// If parent is found, create a child
let child = Keyword {
name: String::from("CHILD"),
code: 1,
parent: Some(parent),
};
hierarchy.add_keyword(child);
second_if = false;
Related
There is a blog post about Rust, where it is explained why Rust can't have self-referencing members.
There is an example with a slice storing offset and length instead of the pointer. I want to apply this to an object, where different parts need references to other parts.
Let's say the object looks like this:
struct Whole {
struct Part0 { //members}
struct Part1 {
needs reference to part 0
}
struct Part2 {
needs reference to part 1 and part 0
}
}
So, Whole can move around in memory (e.g., it can be stored in a vector). But in my scenario, Part1 and Part2 are always inside the Whole (and it is known at compile time). Also, Part1 always needs reference to the Whole it is currently part of. Thus Part 1 should be able to reference Part 0 by calculating its offset from Part 1's self.
I want to make a "reference" that stores offset of Part0 relative from Part1, so that Part1 can get a reference to Part0 by taking this offset from its 'self'.
You could just use Rc:
use std::rc::Rc;
struct Part0 {}
struct Part1 {
at_0: Rc<Part0>,
}
struct Part2 {
at_0: Rc<Part0>,
at_1: Rc<Part1>,
}
struct Whole {
at_0: Rc<Part0>,
at_1: Rc<Part1>,
at_2: Rc<Part2>,
}
fn main() {
let at_0 = Rc::new(Part0 {});
let at_1 = Rc::new(Part1 { at_0: at_0.clone() });
let at_2 = Rc::new(Part2 {
at_0: at_0.clone(),
at_1: at_1.clone(),
});
let _ = Whole { at_0, at_1, at_2 };
}
Playground
But in my scenario, Part1 and Part2 are always inside the Whole (and it is known at compile time).
In that case, there's no reason for Part1 and Part2 to be structs of their own: their fields could instead be defined directly on Whole. Defining them as structs of their own might have made sense if that encapsulated some sub-behaviour, but the fact these parts need access to their siblings belies the suitability of such an approach.
If, for some reason, Part1 and Part2 must nevertheless remain as structs in their own right, then you could:
Pass a reference to the relevant sibling into any method that requires it:
impl Part1 {
fn do_something(&self, part0: &Part0) {
// do whatever with self and part0
}
}
and/or
Implement relevant behaviours on Whole rather than the sub-part:
impl Whole {
fn do_something(&self) {
// do whatever with self.part1 and self.part0
}
}
Of course, using unsafe code, it is nevertheless possible to discover the layout of a struct and access its members in the manner that you describe: I just don't see it being useful for the scenario described in this question. Nevertheless...
Ideally you'd want to embed knowledge of the layout of Whole's members directly into your compiled code. Unfortunately, because rustc does not directly provide means by which the layout of a struct's members can be discovered during compilation, the only way this could be done is by manually calculating the position of each member. However, as documented in The Default Representation in The Rust Reference (emphasis added):
Nominal types without a repr attribute have the default representation. Informally, this representation is also called the rust representation.
There are no guarantees of data layout made by this representation.
Consequently, if Whole is using Rust's default representation, it is impossible to determine the layout of its members during compilation (however, if Whole uses The C Representation, which guarantees a particular layout of a struct's members, one can manually calculate their positions at compile-time, which the repr_offset crate greatly simplifies); instead, the layout of Whole's members can be determined at runtime:
use std::ptr;
impl Whole {
fn new() -> Self {
let mut me = Self { ... };
unsafe {
let addr_of_part0 = ptr::addr_of!(me.part0).cast::<u8>();
let addr_of_part1 = ptr::addr_of!(me.part1).cast::<u8>();
let addr_of_part2 = ptr::addr_of!(me.part2).cast::<u8>();
me.part1.offset_to_part0 = addr_of_part0.offset_from(addr_of_part1);
me.part2.offset_to_part0 = addr_of_part0.offset_from(addr_of_part2);
me.part2.offset_to_part1 = addr_of_part1.offset_from(addr_of_part2);
}
me
}
}
And then one can do:
impl Part1 {
unsafe fn part0(&self) -> &Part0 {
let addr_of_self = ptr::addr_of!(*self).cast::<u8>();
let addr_of_part0 = addr_of_self.offset(self.offset_of_part0);
&*addr_of_part0.cast()
}
}
impl Part2 {
unsafe fn part0(&self) -> &Part0 {
let addr_of_self = ptr::addr_of!(*self).cast::<u8>();
let addr_of_part0 = addr_of_self.offset(self.offset_of_part0);
&*addr_of_part0.cast()
}
unsafe fn part1(&self) -> &Part1 {
let addr_of_self = ptr::addr_of!(*self).cast::<u8>();
let addr_of_part1 = addr_of_self.offset(self.offset_of_part1);
&*addr_of_part1.cast()
}
}
Note that these functions are unsafe because they rely on you, the programmer, only invoking them when you know that the respective offset_of_... fields are valid offsets to the relevant type that can be dereferenced into a shared pointer with the same lifetime as self. You likely will only ever be able to guarantee this when you are accessing self through an owning Whole, otherwise you could easily end up violating memory safety and triggering undefined behaviour—#Netwave's suggestion of using refcounted ownership is one thoroughly sensible way to avoid this risk.
you can use some black magic of unsafe, and encapsulate the logic private to your struct promise SAFETY internally
#[derive(Debug)]
struct Part0 {
data: Vec<u8>,
}
struct Part1 {
pdata0: NonNull<Part0>
}
struct Part2 {
pdata0: NonNull<Part0>,
pdata1: NonNull<Part1>,
}
struct Whole {
p0: Part0,
p1: Part1,
p2: Part2,
}
impl Whole {
fn new() -> Pin<Box<Self>> {
// init Whole with NonNull
let res = Self {
p0: Part0 {
data: vec![1, 2, 3],
},
p1: Part1 {
pdata0: NonNull::dangling(),
},
p2: Part2 {
pdata0: NonNull::dangling(),
pdata1: NonNull::dangling(),
},
};
// get boxed Whole
let mut boxed = Box::pin(res);
// get rawpointer of p0
let ref0 = NonNull::from(&boxed.p0);
// set p0 to p1.pdata0
let mut_ref = (&mut boxed).as_mut();
mut_ref.get_mut().p1 = Part1 {
pdata0: ref0
};
let ref1 = NonNull::from(&boxed.p1);
let mut_ref = (&mut boxed).as_mut();
mut_ref.get_mut().p2 = Part2 {
pdata0: ref0,
pdata1: ref1,
};
boxed
}
fn use_p1(self: &Pin<Box<Self>>) {
unsafe {println!("{:?}", self.p1.pdata0.as_ref())}
}
}
fn main() {
let mut h = Whole::new();
h.use_p1();
}
This question already has answers here:
How can I conditionally provide a default reference without performing unnecessary computation when it isn't used?
(2 answers)
Closed 2 years ago.
I have a function which takes a reference and a condition. If the condition is false, it uses the passed in reference, but when the condition is true, it use a temp reference pointing to the local variable in the function.
The Foo struct is NOT Copy or Clone.
The commented-off code won't compile and I have an ugly way to work around this problem:
#[derive(Debug)]
struct Foo {
a: String,
}
fn f(r: &Foo, cond: bool) {
// let a = if cond { &Foo { a: "456".into() } } else { r };
// use a temp variable `t` can solve this problem in this demo code,
// but what if in production code where build tmp var `t` is expensive?
let mut a = r;
let t = &Foo { a: "456".into() };
if cond {
a = &t;
}
// use the ref here, inside the function.
println!("{:#?}", a);
}
fn main() {
let a = Foo { a: "123".into() };
f(&a, true);
}
Because the use of the reference is limited in the function, is there a way to tell the compiler: do not free the temp var created in the if branch, keep it until the function return and destroy it when destroy the stack frame?
I think there should be a way without using heap memory. On the other hand, when using Box, the type produced in the two if branch will be different, one is &Foo and another is Box<Foo>, so this won't be the right way.
Cow to the rescue! Cow is an enum that represents either (1) a thing that we own or (2) a thing we have borrowed.
#[derive(Debug, Clone)]
struct Foo {
a: String,
}
fn f(r: &Foo, cond: bool) {
use std::borrow::Cow;
let a: Cow<Foo> = if cond {
// (1) a thing that we own, or
Cow::Owned(Foo { a: "456".into() })
} else {
// (2) a thing we have borrowed
Cow::Borrowed(r)
};
// use the ref here, inside the function.
println!("{:#?}", a);
// if a is a Cow::Owned, the owned thing is dropped properly
// if a is a Cow::Borrowed, the reference is dropped.
}
fn main() {
let a = Foo { a: "123".into() };
f(&a, true);
}
Cow requires Clone, however, which is a little bit surplus to what we already have, but it's not a terribly onerous burden.
I am trying to implement Red-Black Tree in Rust. After 2 days of battling with the compiler, I am ready to give up and am here asking for help.
This question helped me quite a bit: How do I handle/circumvent "Cannot assign to ... which is behind a & reference" in Rust?
I looked at existing sample code for RB-Trees in Rust, but all of the ones I saw use some form of unsafe operations or null, which we are not supposed to use here.
I have the following code:
#[derive(Debug, Clone, PartialEq)]
pub enum Colour {
Red,
Black,
}
type T_Node<T> = Option<Box<Node<T>>>;
#[derive(Debug, Clone, PartialEq)]
pub struct Node<T: Copy + Clone + Ord> {
value: T,
colour: Colour,
parent: T_Node<T>,
left: T_Node<T>,
right: T_Node<T>,
}
impl<T: Copy + Clone + Ord> Node<T>
{
pub fn new(value: T) -> Node<T>
{
Node {
value: value,
colour: Colour::Red, // add a new node as red, then fix violations
parent: None,
left: None,
right: None,
// height: 1,
}
}
pub fn insert(&mut self, value: T)
{
if self.value == value
{
return;
}
let mut leaf = if value < self.value { &mut self.left } else { &mut self.right };
match leaf
{
None =>
{
let mut new_node = Node::new(value);
new_node.parent = Some(Box::new(self));
new_node.colour = Colour::Red;
(*leaf) = Some(Box::new(new_node));
},
Some(ref mut leaf) =>
{
leaf.insert(value);
}
};
}
}
The line new_node.parent = Some(Box::new(self)); gives me the error.
I understand understand why the error happens (self is declared as a mutable reference) and I have no idea how to fix this, but I need self to be a mutable reference so I can modify my tree (unless you can suggest something better).
I tried to declare the T_Node to have a mutable reference instead of just Node, but that just created more problems.
I am also open to suggestions for a better choice of variable types and what not.
Any help is appreciated.
There are some faults in the design which makes it impossible to go any further without making some changes.
First, Box doesn't support shared ownership but you require that because the same node is referenced by parent (rbtree.right/rbtree.left) and child (rbtree.parent). For that you need Rc.
So instead of Box, you will need to switch to Rc:
type T_Node<T> = Option<Rc<Node<T>>>;
But this doesn't solve the problem. Now your node is inside Rc and Rc doesn't allow mutation to it's contents (you can mutate by get_mut but that requires it to be unique which is not a constant in your case). You won't be able to do much with your tree unless you can mutate a node.
So you need to use interior mutability pattern. For that we will add an additional layer of RefCell.
type T_Node<T> = Option<Rc<RefCell<Node<T>>>>;
Now, this will allow us to mutate the contents inside.
But this doesn't solve it. Because you need to hold a reference from the child to the parent as well, you will end up creating a reference cycle.
Luckily, the rust book explains how to fix reference cycle for the exact same scenario:
To make the child node aware of its parent, we need to add a parent field to our Node struct definition. The trouble is in deciding what the type of parent should be. We know it can’t contain an Rc, because that would create a reference cycle with leaf.parent pointing to branch and branch.children pointing to leaf, which would cause their strong_count values to never be 0. Thinking about the relationships another way, a parent node should own its children: if a parent node is dropped, its child nodes should be dropped as well. However, a child should not own its parent: if we drop a child node, the parent should still exist. This is a case for weak references!
So we need child to hold a weak reference to parent. This can be done as:
type Child<T> = Option<Rc<RefCell<Node<T>>>>;
type Parent<T> = Option<Weak<RefCell<Node<T>>>>;
Now we have fixed majority of the design.
One more thing that we should do is, instead of exposing Node directly, we will encapsulate it in a struct RBTree which will hold the root of the tree and operations like insert, search, delete, etc. can be called on RBtree. This will make things simple and implementation will become more logical.
pub struct RBTree<T: Ord> {
root: Child<T>,
}
Now, let's write an insert implementation similar to yours:
impl<T: Ord> RBTree<T> {
pub fn insert(&mut self, value: T) {
fn insert<T: Ord>(child: &mut Child<T>, mut new_node: Node<T>) {
let child = child.as_ref().unwrap();
let mut child_mut_borrow = child.borrow_mut();
if child_mut_borrow.value == new_node.value {
return;
}
let leaf = if child_mut_borrow.value > new_node.value {
&mut child_mut_borrow.left
} else {
&mut child_mut_borrow.right
};
match leaf {
Some(_) => {
insert(leaf, new_node);
}
None => {
new_node.parent = Some(Rc::downgrade(&child));
*leaf = Some(Rc::new(RefCell::new(new_node)));
}
};
}
let mut new_node = Node::new(value);
if self.root.is_none() {
new_node.parent = None;
self.root = Some(Rc::new(RefCell::new(new_node)));
} else {
// We ensure that a `None` is never sent to insert()
insert(&mut self.root, new_node);
}
}
}
I defined an insert function inside the RBTree::insert just for the sake of simplicity of recursive calls. The outer functions tests for root and further insertions are carried out inside nested insert functions.
Basically, we start with:
let mut new_node = Node::new(value);
This creates a new node.
Then,
if self.root.is_none() {
new_node.parent = None;
self.root = Some(Rc::new(RefCell::new(new_node)));
} else {
// We ensure that a `None` is never sent to insert()
insert(&mut self.root, new_node);
}
If root is None, insert at root, otherwise call insert with root itself. So the nested insert function basically receives the parent in which left and right child are checked and the insertion is made.
Then, the control moves to the nested insert function.
We define the following two lines for making it convenient to access inner data:
let child = child.as_ref().unwrap();
let mut child_mut_borrow = child.borrow_mut();
Just like in your implementation, we return if value is already there:
if child_mut_borrow.value == new_node.value {
return;
}
Now we store a mutable reference to either left or right child:
let leaf = if child_mut_borrow.value > new_node.value {
&mut child_mut_borrow.left
} else {
&mut child_mut_borrow.right
};
Now, a check is made on the child if it is None or Some. In case of None, we make the insertion. Otherwise, we call insert recursively:
match leaf {
Some(_) => {
insert(leaf, new_node);
}
None => {
new_node.parent = Some(Rc::downgrade(&child));
*leaf = Some(Rc::new(RefCell::new(new_node)));
}
};
Rc::downgrade(&child) is for generating a weak reference.
Here is a working sample: Playground
I know why Rust doesn't like my code. However, I don't know what would be the idiomatic Rust approach to the problem.
I'm a C# programmer, and while I feel I understand Rust's system, I think my "old" approach to some problems don't work in Rust at all.
This code reproduces the problem I'm having, and it probably doesn't look like idiomatic Rust (or maybe it doesn't even look good in C# as well):
//a "global" container for the elements and some extra data
struct Container {
elements: Vec<Element>,
global_contextual_data: i32,
//... more contextual data fields
}
impl Container {
//this just calculates whatever I need based on the contextual data
fn calculate_contextual_data(&self) -> i32 {
//This function will end up using the elements vector and the other fields as well,
//and will do some wacky maths with it.
//That's why I currently have the elements stored in the container
}
}
struct Element {
element_data: i32,
//other fields
}
impl Element {
//I need to take a mutable reference to update element_data,
//and a reference to the container to calculate something that needs
//this global contextual data... including the other elements, as previously stated
fn update_element_data(&mut self, some_data: i32, container: &Container) {
self.element_data *= some_data + container.calculate_contextual_data() //do whatever maths I need
}
}
fn main(){
//let it be mutable so I can assign the elements later
let mut container = Container {
elements: vec![],
global_contextual_data: 1
};
//build a vector of elements
let elements = vec![
Element {
element_data: 5
},
Element {
element_data: 7
}
];
//this works
container.elements = elements;
//and this works, but container is now borrowed as mutable
for elem in container.elements.iter_mut() {
elem.element_data += 1; //and while this works
let some_data = 2;
//i can't borrow it as immutable here and pass to the other function
elem.update_element_data(some_data, &container);
}
}
I understand why elem.update_element_data(some_data, &container); won't work: I'm already borrowing it as mutable when I call iter_mut. Maybe each element should have a reference to the container? But then wouldn't I have more opportunities to break at borrow-checking?
I don't think it's possible to bring my old approach to this new system. Maybe I need to rewrite the whole thing. Can someone point me to the right direction? I've just started programming in Rust, and while the ownership system is making some sort of sense to me, the code I should write "around" it is still not that clear.
I came across this question:
What's the Rust way to modify a structure within nested loops? which gave me insight into my problem.
I revisited the problem and boiled the problem down to the sharing of the vector by borrowing for writes and reads at the same time. This is just forbidden by Rust. I don't want to circumvent the borrow checker using unsafe. I was wondering, though, how much data should I copy?
My Element, which in reality is the entity of a game (I'm simulating a clicker game) has both mutable and immutable properties, which I broke apart.
struct Entity {
type: EntityType,
starting_price: f64,
...
...
status: Cell<EntityStatus>
}
Every time I need to change the status of an entity, I need to call get and set methods on the status field. EntityStatus derives Clone, Copy.
I could even put the fields directly on the struct and have them all be Cells but then it would be cumbersome to work with them (lots of calls to get and set), so I went for the more aesthetically pleasant approach.
By allowing myself to copy the status, edit and set it back, I could borrow the array immutably twice (.iter() instead of .iter_mut()).
I was afraid that the performance would be bad due to the copying, but in reality it was pretty good once I compiled with opt-level=3. If it gets problematic, I might change the fields to be Cells or come up with another approach.
Just do the computation outside:
#[derive(Debug)]
struct Container {
elements: Vec<Element>
}
impl Container {
fn compute(&self) -> i32 {
return 42;
}
fn len(&self) -> usize {
return self.elements.len();
}
fn at_mut(&mut self, index: usize) -> &mut Element {
return &mut self.elements[index];
}
}
#[derive(Debug)]
struct Element {
data: i32
}
impl Element {
fn update(&mut self, data: i32, computed_data: i32) {
self.data *= data + computed_data;
}
}
fn main() {
let mut container = Container {
elements: vec![Element {data: 1}, Element {data: 3}]
};
println!("{:?}", container);
for i in 0..container.len() {
let computed_data = container.compute();
container.at_mut(i).update(2, computed_data);
}
println!("{:?}", container);
}
Another option is to add an update_element to your container:
#[derive(Debug)]
struct Container {
elements: Vec<Element>
}
impl Container {
fn compute(&self) -> i32 {
let sum = self.elements.iter().map(|e| {e.data}).reduce(|a, b| {a + b});
return sum.unwrap_or(0);
}
fn len(&self) -> usize {
return self.elements.len();
}
fn at_mut(&mut self, index: usize) -> &mut Element {
return &mut self.elements[index];
}
fn update_element(&mut self, index: usize, data: i32) {
let computed_data = self.compute();
self.at_mut(index).update(data, computed_data);
}
}
#[derive(Debug)]
struct Element {
data: i32
}
impl Element {
fn update(&mut self, data: i32, computed_data: i32) {
self.data *= data + computed_data;
}
}
fn main() {
let mut container = Container {
elements: vec![Element {data: 1}, Element {data: 3}]
};
println!("{:?}", container);
for i in 0..container.len() {
let computed_data = container.compute();
container.at_mut(i).update(2, computed_data);
}
println!("{:?}", container);
for i in 0..container.len() {
container.update_element(i, 2);
}
println!("{:?}", container);
}
Try it!
Most of this is boilerplate, provided as a compilable example. Scroll down.
use std::rc::{Rc, Weak};
use std::cell::RefCell;
use std::any::{Any, AnyRefExt};
struct Shared {
example: int,
}
struct Widget {
parent: Option<Weak<Box<Widget>>>,
specific: RefCell<Box<Any>>,
shared: RefCell<Shared>,
}
impl Widget {
fn new(specific: Box<Any>,
parent: Option<Rc<Box<Widget>>>) -> Rc<Box<Widget>> {
let parent_option = match parent {
Some(parent) => Some(parent.downgrade()),
None => None,
};
let shared = Shared{pos: 10};
Rc::new(box Widget{
parent: parent_option,
specific: RefCell::new(specific),
shared: RefCell::new(shared)})
}
}
struct Woo {
foo: int,
}
impl Woo {
fn new() -> Box<Any> {
box Woo{foo: 10} as Box<Any>
}
}
fn main() {
let widget = Widget::new(Woo::new(), None);
{
// This is a lot of work...
let cell_borrow = widget.specific.borrow();
let woo = cell_borrow.downcast_ref::<Woo>().unwrap();
println!("{}", woo.foo);
}
// Can the above be made into a function?
// let woo = widget.get_specific::<Woo>();
}
I'm learning Rust and trying to figure out some workable way of doing a widget hierarchy. The above basically does what I need, but it is a bit cumbersome. Especially vexing is the fact that I have to use two statements to convert the inner widget (specific member of Widget). I tried several ways of writing a function that does it all, but the amount of reference and lifetime wizardry is just beyond me.
Can it be done? Can the commented out method at the bottom of my example code be made into reality?
Comments regarding better ways of doing this entire thing are appreciated, but put it in the comments section (or create a new question and link it)
I'll just present a working simplified and more idiomatic version of your code and then explain all the changed I made there:
use std::rc::{Rc, Weak};
use std::any::{Any, AnyRefExt};
struct Shared {
example: int,
}
struct Widget {
parent: Option<Weak<Widget>>,
specific: Box<Any>,
shared: Shared,
}
impl Widget {
fn new(specific: Box<Any>, parent: Option<Rc<Widget>>) -> Widget {
let parent_option = match parent {
Some(parent) => Some(parent.downgrade()),
None => None,
};
let shared = Shared { example: 10 };
Widget{
parent: parent_option,
specific: specific,
shared: shared
}
}
fn get_specific<T: 'static>(&self) -> Option<&T> {
self.specific.downcast_ref::<T>()
}
}
struct Woo {
foo: int,
}
impl Woo {
fn new() -> Woo {
Woo { foo: 10 }
}
}
fn main() {
let widget = Widget::new(box Woo::new() as Box<Any>, None);
let specific = widget.get_specific::<Woo>().unwrap();
println!("{}", specific.foo);
}
First of all, there are needless RefCells inside your structure. RefCells are needed very rarely - only when you need to mutate internal state of an object using only & reference (instead of &mut). This is useful tool for implementing abstractions, but it is almost never needed in application code. Because it is not clear from your code that you really need it, I assume that it was used mistakingly and can be removed.
Next, Rc<Box<Something>> when Something is a struct (like in your case where Something = Widget) is redundant. Putting an owned box into a reference-counted box is just an unnecessary double indirection and allocation. Plain Rc<Widget> is the correct way to express this thing. When dynamically sized types land, it will be also true for trait objects.
Finally, you should try to always return unboxed values. Returning Rc<Box<Widget>> (or even Rc<Widget>) is unnecessary limiting for the callers of your code. You can go from Widget to Rc<Widget> easily, but not the other way around. Rust optimizes by-value returns automatically; if your callers need Rc<Widget> they can box the return value themselves:
let rc_w = box(RC) Widget::new(...);
Same thing is also true for Box<Any> returned by Woo::new().
You can see that in the absence of RefCells your get_specific() method can be implemented very easily. However, you really can't do it with RefCell because RefCell uses RAII for dynamic borrow checks, so you can't return a reference to its internals from a method. You'll have to return core::cell::Refs, and your callers will need to downcast_ref() themselves. This is just another reason to use RefCells sparingly.