5

I have a plugin system where I pass &dyn Any to a dynamically loaded rust function, but downcasting the reference fails because the TypeIds differ (for the same type), although I added rustflags = ["-Cmetadata=12345678"] to both crates' cargo config. Also it seems as if only types from external crates are affected (I tried () and it yielded the same TypeId in both crates). I am currently casting the raw pointers (unsafe { &*(v as *const dyn Any as *const Type) }) to work around this issue, but I would prefer a solution without unsafe code.

For example the following code:

println!("CRATE 1: TypeId of `()`: `{:?}`, TypeId of `toml::Value`: `{:?}`",
                 TypeId::of::<()>(), TypeId::of::<toml::Value>());

produces this output:

CRATE 1: TypeId of `()`: `TypeId { t: 7549865886324542212 }`, TypeId of `toml::Value`: `TypeId { t: 9270396907601429078 }`
CRATE 2: TypeId of `()`: `TypeId { t: 7549865886324542212 }`, TypeId of `toml::Value`: `TypeId { t: 5704635987193303200 }`

EDIT: This does not seem to be a problem with different dependency versions, as crate 2 (which is dynamically loaded) depends on crate 3 (which is also dynamically loaded) and the problem still occurs, although both, crate 2 and crate 3, are local dependencies (so there is only one version). Crate 1 is btw. the crate that loads crate 2 & 3.

EDIT: I removed the -Cmetadata option from all 3 crates and now I get the same TypeId for toml::Value, however the TypeId of a type in crate 1 that I want to downcast in crate 2 still differs.

TobiP64
  • 146
  • 3
  • 9
  • 2
    Are you sure this isn't a crate version issue? E.g., crate 1 requires `toml` 0.4.5 and crate 2 requires 0.4.8, or something like that? – trent Aug 17 '19 at 13:08
  • This was my first assumption, but I cleaned, updated and rebuilt all crates. I also checked the dependencies and the version is in all 3 crates the same. – TobiP64 Aug 17 '19 at 13:21
  • 1
    I don't believe any `-C` options will have an effect on type IDs; those are for codegen. Type IDs are resolved inside rustc itself, before it hands off to LLVM. – trent Aug 17 '19 at 15:26
  • I read somewhere that it affects the `TypeId` gen. Anyway, I removed this option from all 3 crates and now I get the same `TypeId` for `toml::Value`, but not for a type in crate 3 that I want to downcast in crate 2. So it actually affects the `TypeId`, but not the way I thought it does. – TobiP64 Aug 18 '19 at 08:19
  • Due to my own incompetence I forgot to recompile one crate. Now only the `TypeId` of a type in the loader crate still differs. – TobiP64 Aug 18 '19 at 09:03

1 Answers1

2

After some testing I found out that the TypeIds are different because the loader crate (crate 1) is used as a library in the other 2 crates but executed as a binary.

To work around the issue I extracted all of crate 1's types I wanted to use in the loaded crates to a new crate and added it to each crate's dependencies. This new crate is only ever used as a library and not a binary, thus the TypeIds should be consistent.

A summary of everything I had to do to get it working:

  • use the same toolchain version for all crates
  • use the same dependency versions in all crates
  • do not use -Cmetadata, this doesn't work anymore and has in fact the opposite effect
  • extract all types that are used in the loader crate and the loaded crates into a new crate and add it to all crates' dependencies
  • whenever you change something in this 'common types crate' you have to recompile all the other crates, so the TypeIdS are up to date

If you still have a problem you can fall back to unsafe rust (e.g. unsafe { &*(value as *const dyn Any as *const Type) }). Note that there are no checks at all, so you might encounter segmentation faults if the types don't match.

Special thanks to @trentcl for pointing me in the right direction.

TobiP64
  • 146
  • 3
  • 9