[Solved] How to implement a generic Dummy?


#1

My first project for learning Rust is a highly dynamic layout engine using termion. The idea is to have a tree of handlers implementing different traits. Unfortunately I hit one of the dark corners of Rust because each handler needs a link to its parent. Because all handlers are boxed, a plain ptr (*const Handler) can safely be used. To initialize the parent ptr I use a Dummy:

[code]pub trait Widget {
fn init(&mut self);
// …
}

pub struct Dummy { }

impl Dummy {
pub fn new() -> Box {
let t = Dummy {};
Box::new(t) as Box
}
}

impl Widget for Dummy {
fn init(&mut self){ panic!(Dummy called!); }
// …
}[/code]
Since I need a small number of other handlers beside Widget I would like to have a generic Dummy but cannot figure out how to implement this. I could not find an example, may be because I’m not yet familiar enough with Rust. Any help or pointers to code are appreciated!

Peter


#2

I don’t really get your problem, but for your Idea with using a plain ptr:
This seems safe at first but allows for two things that are not rusty:

  • A Box go out of scope before it’s child -> Dangeling pointer
  • A owned Box may be modified while at the same time a (const) reference to it exists.
    Normally Rust would not compile a program where this is possible.
    So I recommend using Rc<RefCell<Widget>> instead of Box<Widget>. Use a weak pointer to that Rc-Box (instead of the const* you had in mind) within the child widgets as parent pointer.

You can use playpen to clarify your problem

https://play.rust-lang.org/?gist=ba47ae9a489e69caaa7915c892658c05&version=stable&backtrace=0


#3

Rc means ref-counted, doesn’t it? That means an unnecessary overhead because the raw ptr will never (!) be borrowed. The question is how to make an generic Dummy which works with different traits, something like Dummy::new() where T is Widget, a Container for Widgets or others. Of course, for each trait an impl is needed but the Interface is coherent. Here is how I want to use the dummy:

[code]pub struct Terminal {
bounds: Rect,
menu: Box,
// … omitted
}

impl Terminal { // terminal is a Window
pub fn new() -> Box {
let (w,h) = terminal_size().unwrap();
let t = Terminal {
bounds: Rect::new (1,1,w,h),
menu: Dummy::new(),
};
let mut terminal = Box::new(t) as Box;
terminal.init();
terminal
}
}

impl Window for Terminal {
fn init(&mut self){
self.menu = Menu::new(self, … omitted…);
// …
}
}[/code]

Menu is a widget and has a raw ptr to its parent and is initialized accordingly.

[code]pub struct Menu{
parent: *const Window,
// …}

impl Menu {
pub fn new (parent: *const Window, … omitted … ) -> Box {

}

}
[/code]


#4

Sorry, there is a typo:

That should be: … something like Dummy::new() where T is Widget, a Container for Widgets or others.


#5

...Dummy<T> ... of course... :disappointed:


#6

You want this:

use std::marker::PhantomData;

struct Dummy<T: ?Sized>(PhantomData<T>);

impl<T: ?Sized> Dummy<T> {
    fn new() -> Dummy<T> {
        Dummy(PhantomData)
    }
}

The ?Sized bound is necessary because trait object types like Widget do not implement the built-in trait Sized, because they do not have a statically determined size.


#7

Thanks a lot, that does it! I’ve tried various ways to get a version of Dummy::new() to return a Box<T> but failed for one reason or another.

impl<T: ?Sized> Dummy<T> { fn new() -> Box<T>{ // ??? } }
Not that important, but I would like to know to understand why I failed.


#8

This code in general suggests you’re thinking about this in terms of “object oriented / inheritance” mechanism. A Dummy<T> is not a subclass of T; remember that T is a type and not a trait and could be, for example, u32. How would you go from a Dummy<u32> to a Box<u32>, for example? You can’t because you don’t have any u32. The type system doesn’t distinguish between trait object types and other types.

There might be some very hacky ways get the behavior you want (restrict T to a trait object type & perform the cast) but in my opinion you are much better off just writing Box::new(Dummy::new()) as Box<Widget>.


#9

Thanks for explanation! I’ve to think about it …