*const T has quite a few methods that seem like they don't need to require T: Sized
Not sure which exactly, but we should evaluate and relax bounds where possible.
See also https://github.com/rust-lang/rust/issues/24794, https://github.com/rust-lang/rust/issues/36952.
Adding to this, std::ptr::null & std::ptr::null_mut. Technically not methods on *const T but has these bounds too.
Steven Malis at 2017-07-25 22:18:03
All inherent methods and trait impls for
*const Tand*mut Tnow have aT: ?Sizedbound thanks to #44932 and #46094.Following the paper trail, it appears that there may be good reason that
std::ptr::{null,null_mut}do not supportT: ?Sized.Michael Lamparski at 2018-02-21 00:20:57
Should I open a new issue to argue about
std::ptr::null/std::ptr::null_mut? Seems wrong when used with extern types:extern { type foo_t; } fn fake_foo_new() -> *const foo_t { std::ptr::null() }error[E0277]: the trait bound `foo_t: std::marker::Sized` is not satisfied --> src/main.rs:15:5 | 15 | std::ptr::null() | ^^^^^^^^^^^^^^ `foo_t` does not have a constant size known at compile-time | = help: the trait `std::marker::Sized` is not implemented for `foo_t` = note: required by `std::ptr::null`Jake Goulding at 2018-04-30 21:03:32
Do mind that the conclusion drawn in the last thread I linked seemed to be that null vtable pointers (even on raw pointers) are currently considered to be UB in rust.
(I'm not saying that you necessarily shouldn't; just pointing out. Maybe new facts and use cases may bring a new perspective)
Edit: wow, it just occurred to me what you're showing in that example. I never realized that rust had any unsized types that use thin pointers!
Michael Lamparski at 2018-04-30 23:20:21
I'll be that person and ping in @dtolnay, @eddyb, and @mikeyhew for some more discussion around my last comment
Jake Goulding at 2018-05-05 15:06:55
Unless there's a trait to distinguish between thin+unsized and fat+unsized pointers, we cannot relax the
Sizedbounds. In particular, trait object pointers*const dyn Traitmust not have a null vtable (unless we haveimpl Trait for !for all traits), and thusnull()should not be defined for trait objects.When the custom DST traits are included we could define
fn null<T: ?Sized>() -> *const T where T::Meta: Default, { /* assemble a custom DST from { ptr = 0, meta = T::Meta::default() } */ }kennytm at 2018-05-05 15:21:57
I can certainly see that a null vtable would be a bad thing, but I also feel that the complete inability to create a
<s>NULL<s>or uninitialized</s> extern type is going to be a real blocker to a lot of usage of extern types:#![feature(extern_types)] extern { type foo_t; fn foo_init(f: *mut foo_t); } fn main() { let mut a = std::mem::uninitialized(); unsafe { foo_init(&mut a) }; }
</s>error[E0277]: the trait bound `foo_t: std::marker::Sized` is not satisfied --> src/main.rs:10:9 | 10 | let mut a = std::mem::uninitialized(); | ^^^^^ `foo_t` does not have a constant size known at compile-time | = help: the trait `std::marker::Sized` is not implemented for `foo_t` = note: all local variables must have a statically known sizeEdit — that was a dumb example because of course
acan't be on the stack, it's unsized. The real example of that would work:#![feature(extern_types)] extern "C" { type foo_t; fn foo_init(f: *mut *mut foo_t); } fn main() { unsafe { let mut a = std::mem::uninitialized(); foo_init(&mut a); } }The corresponding equivalent with
nullstill doesn't:#![feature(extern_types)] extern "C" { type foo_t; fn foo_init(f: *mut *mut foo_t); } fn main() { unsafe { let mut a = std::ptr::null_mut(); foo_init(&mut a); } }error[E0277]: the trait bound `foo_t: std::marker::Sized` is not satisfied --> src/main.rs:11:21 | 11 | let mut a = std::ptr::null_mut(); | ^^^^^^^^^^^^^^^^^^ `foo_t` does not have a constant size known at compile-time | = help: the trait `std::marker::Sized` is not implemented for `foo_t` = note: required by `std::ptr::null_mut`Jake Goulding at 2018-05-05 17:56:42
As a workaround for now you can get a null pointer to extern type through
as:#![feature(extern_types)] extern "C" { type foo_t; fn foo_take_null(f: *mut foo_t); } fn main() { let a = 0 as *mut foo_t; unsafe { foo_take_null(a); } }David Tolnay at 2018-05-05 18:06:57
We should add a private/unstable marker trait and use it to add a bound that's weaker than
Sized.It should be directly equivalent to
T::Meta == (). The implementation can use unions and an assert that the size of the pointer matches that of a regular pointer (which it always would for this trait).cc @retep998
Eduard-Mihai Burtescu at 2018-07-31 11:29:31
Is
nullandnull_mutfor extern types the only remaining thing tracked in this issue?
RFC 2580 (implementation PR: https://github.com/rust-lang/rust/pull/81172) add public APIs for pointer metadata and a
pub trait Thin = Pointee<Metadata = ()>;.Building on top of that, I hoped that fixing this issue would be as simple as
-pub const fn null<T>() -> *const T { +pub const fn null<T: ?Sized + Thin>() -> *const T { -pub const fn null_mut<T>() -> *mut T { +pub const fn null_mut<T: ?Sized + Thin>() -> *mut T {However this gives two kinds of errors when compiling libcore:
-
error[E0606]: casting `usize` as `*const T` is invalid --> library/core/src/ptr/mod.rs:210:5 | 210 | 0 as *const T | ^^^^^^^^^^^^^ error[E0606]: casting `usize` as `*mut T` is invalid --> library/core/src/ptr/mod.rs:228:5 | 228 | 0 as *mut T | ^^^^^^^^^^^Possible fix: in
compiler/rustc_typeck/src/check/cast.rs, inFnCtxt::pointer_kind, in thety::Paramcase: if aPointee<Metadata = ()>bound is in scope for that parameter returnOk(Some(PointerKind::Thin)). This might be similar totype_is_known_to_be_sized_modulo_regionswhich for type parameters presumably checks if a (possibly implicity)Sizedbound is in scope. -
error[E0271]: type mismatch resolving `<T as Pointee>::Metadata == ()` --> library/core/src/sync/atomic.rs:171:24 | 171 | AtomicPtr::new(crate::ptr::null_mut()) | ^^^^^^^^^^^^^^^^^^^^ expected `()`, found associated type | ::: library/core/src/ptr/mod.rs:227:35 | 227 | pub const fn null_mut<T: ?Sized + Thin>() -> *mut T { | ---- required by this bound in `ptr::null_mut` | = note: expected unit type `()` found associated type `<T as Pointee>::Metadata` = help: consider constraining the associated type `<T as Pointee>::Metadata` to `()` = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced>Possible fix? “Somehow” teach the trait resolver that
T: SizedimpliesT: Pointee<Metadata = ()>. Maybe in the compiler itself, as changing the library definition totrait Sized: Thin {}causes many instances oferror[E0271]: type mismatch resolving `<Something as Pointee>::Metadata == ()`.
Simon Sapin at 2021-01-18 19:37:49
-
I hope we can do
trait Sized: Thin {}in the library in addition to whatever compiler tricks are needed, as the supertrait is "morally correct" and good for documentation.John Ericson at 2021-01-18 19:49:00
AtomicPtrspecifically should probably have the sameT: ?Sized + Thinbound, so the issue would go away there - but I agree with makingtrait Sized: Thin {}Mads Marquart at 2021-06-15 12:48:01
Related:
pointer::castrequires the generic parameterUto beSized. Is this intentional? I would expect it to be a valid way to cast a pointer to a#[repr(transparent)]type to a pointer to its containing type or the other way around, same as anascast ormem::transmute::<*const ReprTransparentType, *const InnerType>.Jonas Platte at 2021-06-21 10:38:30