I’m trying to understand how trait objects convert to and from raw pointers.

If I try to convert a trait-typed reference to a raw pointer, I get E0495, which complains about lifetimes without reference to the vtable. If I use a struct-typed reference instead, it works, which suggests that the problem is indeed related to the vtable pointer even though the compiler error doesn’t say so.

Note that the Decoder in &'a Decoder is more restrictive than the one in *mut Decoder. Regarding the actual type T of the object (which implements Decoder), the former requires that it outlives 'a but the latter requires that it outlives everything. Your error seems to disappear when you change the return type:

Why doesn’t the compiler complain about improper C types when declaring *mut Decoder as the return value of an extern “C” function?

It probably should issue a warning, or perhaps clippy is the right place for that. Technically, C code can decompose it into the 2 parts and store it as context - then pass it back somewhere else to Rust. Trait object repr isn’t guaranteed however, and so that’s why there probably should be a lint against it.

The ABI is unstable but current impl is known - it’s a fat ptr. The fat ptr is two usize values, the first being a ptr to the data (ie the starting memory address where the value resides) and the second being a ptr to the vtbl. So the C side could be a struct with two *void ptrs, which is basically https://doc.rust-lang.org/std/raw/struct.TraitObject.html.