Description
A grossly simplified illustrative example.
Assume we're in a library crate (I want to get a shared object out of this so I'm using --crate-type dylib
but this appears to be a problem for the general case), and we have the following items defined.
pub enum Action {
Thing,
OtherThing,
}
pub trait Game {
fn legal_action_p(&self, a: &Action) -> bool;
}
If we now wish to expose a function that uses a Game
implementor across the library boundary (if, for example, you wish to dlopen(3)
then dlsym(3)
it), one might expect that they could define a trait bound:
#[no_mangle]
pub extern fn decide_action<T: Game>(g: &T) -> () {
println!("deciding action!");
let a = Action::Thing;
g.legal_action_p(&a);
}
(There's a reason I stuffed a println!
in there.)
Compile: rustc testlib.rs --crate-type dylib -g --out-dir target/debug
, then:
$ nm target/debug/libtestlib.so | grep -i decide_action
00000000003c5110 D _ZN13decide_action15__STATIC_FMTSTR20h722c5e523a62788eMaaE
Which is, of course, the println!
format string. Note a lack of any symbol matching the exact name decide_action
.
OK, but if we put the Game
impl in a Box
:
#[no_mangle]
pub extern fn decide_action(g: &Box<Game>) -> () {
println!("deciding action!");
let a = Action::Thing;
(*g).legal_action_p(&a);
}
Suddenly, it comes back!
$ nm target/debug/libtestlib.so | grep -i decide_action
00000000000ad4b0 T decide_action
00000000000ad4f0 t _ZN13decide_action10__rust_abiE
00000000003c5120 d _ZN13decide_action15__STATIC_FMTSTR20hbe3d66b5baeedebcsbaE
gah!
This presents on stable, beta, and nightly.
This strikes me as a bug, although I'm not sure what in. I suppose you cannot really enforce traits across the extern
boundary (although that would be Nice To Have if there's Rust on the other side). On the other hand, how does a box of trait somehow manage to subvert this? I supose this is a compiler error, but I'm a bit fuzzy about what the error is (I guess that if extern fn
, then if trait bound, then fail, else do the extern fn
).
(As a general note, "FFI" from Rust to Rust, where you explicitly want to plug dylibs, is substantially more uncomfortable than expected.)