use core::mem::MaybeUninit;
pub unsafe trait AsMaybeUninit {
type MaybeUninit: ?Sized;
}
unsafe impl<T: Sized> AsMaybeUninit for T {
type MaybeUninit = MaybeUninit<T>;
}
unsafe impl<T: Sized> AsMaybeUninit for [T] {
type MaybeUninit = [MaybeUninit<T>];
}
unsafe impl AsMaybeUninit for str {
type MaybeUninit = <[u8] as AsMaybeUninit>::MaybeUninit;
}
unsafe trait FromBytes {
unsafe fn read_from_bytes(bytes: &[u8]) -> Option<Self>
where
Self: Sized,
{
fn read_from_bytes_inner<T: AsMaybeUninit<MaybeUninit = MaybeUninit<T>>>(
bytes: &[u8],
) -> Option<T> {
todo!()
}
read_from_bytes_inner(bytes)
}
}
This compiles fine. However, if I change unsafe trait FromBytes to unsafe trait FromBytes: AsMaybeUninit, it fails:
error[E0271]: type mismatch resolving `<Self as AsMaybeUninit>::MaybeUninit == MaybeUninit<Self>`
--> src/lib.rs:32:9
|
32 | read_from_bytes_inner(bytes)
| ^^^^^^^^^^^^^^^^^^^^^ type mismatch resolving `<Self as AsMaybeUninit>::MaybeUninit == MaybeUninit<Self>`
|
note: expected this to be `MaybeUninit<Self>`
--> src/lib.rs:10:24
|
10 | type MaybeUninit = MaybeUninit<T>;
| ^^^^^^^^^^^^^^
= note: expected union `MaybeUninit<Self>`
found associated type `<Self as AsMaybeUninit>::MaybeUninit`
= help: consider constraining the associated type `<Self as AsMaybeUninit>::MaybeUninit` to `MaybeUninit<Self>`
= note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html
note: required by a bound in `read_from_bytes_inner`
--> src/lib.rs:26:51
|
26 | fn read_from_bytes_inner<T: AsMaybeUninit<MaybeUninit = MaybeUninit<T>>>(
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `read_from_bytes_inner`
Why does adding a bound which was already satisfied cause this to break, and is there a way I can fix it? In the use case that this is a minimized version of, I unfortunately need the FromBytes: AsMaybeUninit bound, so I can't just get rid of it.
I believe this is an insufficient normalization issue. Without the Self: AsMaybeUninit bound, calling read_from_bytes_inner sees that Self: Sized and that impl<T: Sized> AsMaybeUninit for T applies, so it can use that impl. When Self: AsMaybeUninit, calling read_from_bytes_inner sees that bound and uses that, and never looks for the fact that the Self: Sized allows it to know which impl applies.
Adding a redundant where Self: AsMaybeUninit<MaybeUninit = MaybeUninit<Self>> bound to read_from_bytes (not the inner one) should allow the code to compile.
Yeah, I tried that too. It works, but unfortunately it bubbles up into other generic uses of FromBytes (given only T: FromBytes + Sized, you can't call read_from_bytes). Ideally I'd figure out a way to avoid changing the signature of read_from_bytes.
I figured out a way around the issue. There's no real analogue in this stripped-down example, so here's the fix in the actual commit I'm working with:
fn try_read_from(bytes: &[u8]) -> Option<Self>
where
Self: Sized,
{
// TODO(https://github.com/rust-lang/rust/issues/115080): Inline this
// function once #115080 is resolved.
#[inline(always)]
fn try_read_from_inner<T: Sized, F: FnOnce(&MaybeValid<T>) -> bool>(
bytes: &[u8],
is_bit_valid: F,
) -> Option<T> {
let maybe_valid = MaybeValid::<T>::read_from(bytes)?;
if is_bit_valid(&maybe_valid) {
Some(unsafe { maybe_valid.assume_valid() })
} else {
None
}
}
try_read_from_inner(bytes, Self::is_bit_valid)
}
The trick is that I actually throw away the AsMaybeUninit bound in try_read_from_inner, and that seems to solve the problem that @CAD97 identified (note that T only has a Sized bound).