Trait with static method is not object-safe?

I'm experimenting with a dynamic registry of different "object" types that all implement a common interface "Obj". I'd like to be able to register them and later create instances depending on user configuration.

This is what I've come up with:

use std::collections::HashMap;

trait Obj {
    fn create(name: &str) -> Box<Obj>;
}

struct ExampleObj {
    name: String,
}

impl Obj for ExampleObj {
    fn create(name: &str) -> Box<Obj> {
        Box::new(ExampleObj { name: name.into() })
    }
}

type Constructor = Fn(&str) -> Box<Obj>;

struct ObjRegistry<'a> {
    map: HashMap<String, &'a Constructor>,
}

impl<'a> ObjRegistry<'a> {
    fn new() -> ObjRegistry<'a> {
        ObjRegistry { map: HashMap::new() }
    }

    fn register(&mut self, clsname: &str, ctor: &'a Constructor) {
        self.map.insert(clsname.into(), ctor);
    }
}

fn main() {
    let mut reg = ObjRegistry::new();
    reg.register("test", &ExampleObj::create);
}

but it doesn't compile because "the trait Obj is not object-safe". How to work around that?

I've also tried making create a free function, but it seems to make the compiler exit with SIGILL...

I don't have any particular insight into the resulting error, but your code does trigger a compiler bug in all of the stable/beta/nightly branches. It looks like the type of error ("Path not fully resolved") has been reported a few times:
https://github.com/rust-lang/rust/issues?utf8=✓&q=is%3Aopen+label%3AI-ICE+path+not+fully+resolved

Because that can't possibly work.

It was decided a while ago to outright forbid traits becoming objects if they had anything that couldn't be in the vtable. People kept getting weird problems where they'd have a trait object, but with half the methods missing and no idea why.

The problem with create specifically is that it's impossible to put in an object. How do you dispatch a call to create when it doesn't take &self? You can't even call it on a trait object; you'd have to know the original concrete type anyway.

The solution is... don't put object-unsafe methods on a trait you intend to use as an object.

At this point, I'd like to show you some working code... but I'm getting that ICE as well, and I'm not sure how to make it stop. Still, hopefully the above is informative, at least. :stuck_out_tongue:

1 Like

Got it. So it should work with a free function, but that requires the bug to be fixed?