Fat raw pointers into fat rust pointers

fn bar<T: ?Sized>(x: &mut T) {
    let x: *mut T = x;
    let _: &mut T = unsafe {x.as_mut().unwrap()};
}
error[E0277]: the trait bound `T: std::marker::Sized` is not satisfied
   --> src/lib.rs:100:31
    |
100 |     let _: &mut T = unsafe {x.as_mut().unwrap()};
    |                               ^^^^^^ `T` does not have a constant size known at compile-time
    |
    = help: the trait `std::marker::Sized` is not implemented for `T`
    = help: consider adding a `where T: std::marker::Sized` bound

From the documentation, it looks like <*mut T>::as_mut supports T: ?Sized (scroll up to the inherent impl's bounds)... However, if you look at the source, you'll see the that there is an explicit T: Sized hiding in as_mut's definition!

What's up with that?

1 Like

Looks like all of the methods in that inherent impl have a Sized restriction, not just as_mut. It does seem strange.

They use is_null(), which compares self == null() or self == null_mut(), but there's no generic way to create such null pointers.

It seems feasible to just compare is_null() as thin pointers, self as *const u8 as usize == 0. That's what Zeroable::is_zero() does for pointers, anyway. I'm going to try this and send a PR if it works out, then is_null(), as_ref(), and as_mut() can be T: ?Sized.

1 Like

https://github.com/rust-lang/rust/pull/44932

2 Likes

is_null is a tricky one isn't it? If you just compare one field (the pointer) to null, then for a fat pointer there are many null values. Might surprise someone down the line, don't know if there is any worse impact than that.

That's true! We could apply the broadened test only for as_ref and as_mut, if that's deemed too surprising, but I think it's still reasonable for is_null. Maybe I'll add a note to the docs that two pointers being null doesn't necessarily imply their equality.