impl<A: Trivial, B: ?Sized> Foo<A, B> {
pub fn from_ref(value: &B) -> &Foo<A, B> {
// check that layout of `A` is zero-sized and 1 aligned
assert_eq!(Layout::new::<A>(), Layout::new::<()>());
// check that there is a valid instance of `A`
// (in case invalid consts lints are ignored)
let _instance = I::INSTANCE;
// suspicious cast
unsafe { &*(value as *const B as *const Self) }
}
}
I think it should be, because
a type with a public canonical instance and zero size can't have any validity/safety invariants
The layouts of B and Foo<A, B> are the same when A has the same layout as () by repr(C)
I'd add a slight nuance here, in that the const is a no-side-effects instance factory, which is more powerful than just an instance.
It would be possible to have a single static of a ZST where that ZST was not Copy, in which case the code would violate safety invariants (though not validity ones) if it duplicated them.
But here you require Copy + const Default (essentially), which I think means you're good. One can think of the _instance you created as being moved into the thing behind the reference, and it's Copy so it's not Drop and thus where/when it drops is not observable.
I think you could simplify this logic a bit by just additionally checking that
I agree it probably shouldn't be necessary, as that's what I expect repr(C) would do, but technically ZSTs are UB in C, so might as well just be explicit about it, since the check will get optimized out anyway.
(Aside: if you don't need generic A, then consider repr(transparent) instead.)
Note: This algorithm can produce zero-sized structs. In C, an empty struct declaration like struct Foo { } is illegal. However, both gcc and clang support options to enable such structs, and assign them size zero. C++, in contrast, gives empty structs a size of 1, unless they are inherited from or they are fields that have the [[no_unique_address]] attribute, in which case they do not increase the overall size of the struct.
So I think I'm good there. But I agree that it does simplify the logic, so I will apply that change.
(Aside: if you don't need generic A , then consider repr(transparent) instead.)
Yep, that would have been my first option, but I do need that generic parameter (it's not always zero-sized!)
I just tried this, but it won't work because B: ?Sized, so Layout::new doesn't work (it requires Sized), and I can't use Layout::for_value, because I don't have an instance of Foo<A, B>