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.
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.
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.
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.