How to turn a &Foo<T> into a &Foo<U> without unsafe code

Is there a way to implement the method without unsafe code? I'm currently transmuting self, but I feel like there should be a safe way since PhantomData is just a compile-time tag.

struct Foo<T> {
    text: String,
    _ty: PhantomData<T>,
}

impl<T> Foo<T> {
    fn change_type<U>(&self) -> &Foo<U> {
        todo!();
    }
}

Playground.

if you have a &Foo<U>, the borrow checker requires the existence of an owned Foo<U> somewhere, so this reference casting will require unsafe somewhere.

Applying #[repr(transparent)] would guarantee that this transmute is safe.

1 Like

Here's a version using unsafe (but not transmute).

See the discussion in this RFC PR for some challenges to making this a safe operation somehow.

2 Likes

Why is repr(transparent) needed here? We need Foo<T>'s layout to be the same as Foo<U>, but isn't it ok if it differs from String's layout?

If not, would casting/transmuting be sound for structs with more than 1 non-ZST field?

repr(transparent) gurantees that ABI of Foo is the same as ABI of String. Without that compiler is completely free to e.g. add random padding fields before or after the contained String as long as String is properly aligned and maybe even has compiler flag to do exactly that.

As for structs with more than 1 non-ZST fields then they must be repr(C) (or anything else which guarantees specific layout), as with default repr layout is not guaranteed and there definitely is a compiler flag to randomize layout. If you do not want to bind ABI of Foo to String then you can use repr(C) in place of repr(transparent) too.

1 Like

The problem is how do you guarantee that Foo<T>'s layout is the same as Foo<U>? With #[repr(transparent)] you guarantee that both layouts are the same as String's, and thus they must also be the same.

You can use #[repr(C)] for those.

5 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.