R. Martinho Fernandes
R. Martinho Fernandes

Reputation: 234654

Can I have a generic type bound that requires that type to be a trait?

I want to declare a generic function that accepts trait objects and only trait objects. I want this because I want to type erase these and pass them as TraitObject objects across an ABI boundary.

A function written like this will fail to compile...

fn f<T: ?Sized>(t: &T) -> std::raw::TraitObject {
    unsafe { std::mem::transmute(t) }
}

... with the following error:

error[E0512]: transmute called with differently sized types: &T (pointer to T) to std::raw::TraitObject (128 bits)

I understand why the compiler complains of different sizes: &T can be a pointer to a concrete type (like &i32), which is a single pointer (64 bits), or a trait object (like &Display), which is going to be two pointers with the same layout as std::raw::TraitObject (128 bits).

This function should be fine as long as &T is a trait object, i.e. T is a trait. Is there a way to express this requirement?

Upvotes: 3

Views: 259

Answers (3)

R. Martinho Fernandes
R. Martinho Fernandes

Reputation: 234654

If you use transmute_copy instead, you can have the compiler ignore the size mismatches. This, however, means you have to handle such issues yourself, by e.g. checking the size yourself and perhaps panicking if there's a mismatch. Not doing so can result in undefined behaviour.

fn f<T: ?Sized>(t: &T) -> std::raw::TraitObject {
    assert!(std::mem::size_of::<&T>() == std::mem::size_of::<std::raw::TraitObject>());
    unsafe { std::mem::transmute_copy(&r) }
}

Upvotes: 3

Matthieu M.
Matthieu M.

Reputation: 300409

It is impossible to prove a negative... but as far as I know the answer is no, sorry.

The representation of TraitObject is unstable, notably because in the future Rust might be able to tack on multiple virtual pointers to a single data pointer (representing &(Display + Eq) for example).

In the mean time, I usually use low-level memory tricks to read the virtual pointer and data pointer then build the TraitObject myself; guarded by a call to mem::size_of to ensure that &T has the right size for 2 *mut () because ?Sized means Sized or not (and not !Sized).

Upvotes: 3

Shepmaster
Shepmaster

Reputation: 432159

I believe the answer is "no":

There is no way to refer to all trait objects generically

(emphasis mine)

Upvotes: 1

Related Questions