-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Default is not implemented for raw pointers (*const and *mut) #2464
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
For some reason I was under the impression this was deliberately not implemented, since NULL isn't the safest thing in the world. Though I don't think I've seen an explicit discussion about that. |
Right, I agree that null feels better when it’s a explicit choice rather than a possibly-derived possibly-not-thought-much-about default. |
There are many better options than raw pointers for code that wants to handle null explicitly, or not at all, ( The whole point of raw pointers is that they can be null, and Honestly, working with raw pointers is always hard, but the only thing adding hoops does for me is requiring me to spend time and effort coming up with workarounds for no good reason. |
Personally, I've used raw pointers mostly in situations where they can't be null. I suppose I could have theoretically used |
It‘s really not. Your statement matches not |
I think this is only (mostly) true in C++, where the various smart pointers introduced in modern versions of the language have retroactively made the more fundamental raw pointers only the best choice when either a) nullability is required or b) the pointer is completely non-owning. For a), I believe that's mainly because For b), that's just because |
These "features" apply to other pointer types like
I feel your pain and I think this is an issue worth solving, but the solution is not to make raw pointers unnecessarily harder to use, but rather to make |
What about Unless you specifically care about layout optimizations, there is no reason to use |
I disqualified these on the grounds that they are not pointers that can point to the null address and that you can dereference. Just because something is layout compatible with a raw pointer does not make it a raw pointer.
I disagree. If you figure that this pointer can never be null you should make it |
I don't understand the claim that But maybe that's the wrong question entirely. I think we're only playing the "what is a pointer?" game because some of us didn't understand your earlier claim that:
Regardless of how we differ in our usage of words like "pointer" or "null", I'm pretty sure that there are non-raw pointer/reference types in Rust which have a special value that means not pointing/referring to anything, and that
Are you trying to say that you actually want to dereference the null value, and that's what makes raw pointers the type of choice? I thought doing that was undefined behavior. |
The difference is that The point I was trying to make is that the only types in Rust that let you store this null address as a value that you can manipulate are raw pointers.
A raw pointer that points to the address null does not point to nothing, it points to an object of some type on the address null. This is why
No, what I said is that these types cannot represent the null address as a value. |
In any case, the only argument I've actually heard against improving the raw pointer ergonomics by implementing traits like It was pointed out that this happens if users expect for raw pointers to never be null, to which I've argued that such cases are bugs because either the user didn't understood that the pointer could be null, or because the user didn't use the appropriate type for their pointer (like, for example, @comex raised a very good point about the bad ergonomics of interfacing these types with raw pointer APIs, but while that's a problem worth solving, it's orthogonal to this one. So I am very unconvinced by the counter argument because at least for the reasons raised, the only situation in which |
|
I think I would be more interested to have a |
@phaazon how is having a default value for pointers different than having a default value for |
It’s not, and to me, |
Ah I see, yes that is fair. I suppose that's just like the For raw pointers the situation is a bit different, and I don't know of any identity element beyond NULL that might make sense (EDIT: at least for the
Do you have any applications in mind in which you would assign |
Yes, but
Why? No default is better than a bad/wrong default.
No, |
Notice that That's a useful and valuable feature, and it doesn't really matter what the value by This does not prevent particular implementations of From this POV, Also, these values do not prevent people from adding others. You can implement a |
(sarcasm) Absolutely safe to make fn foo(x: &i32) -> *mut i32 {
return ((&(*x)) as *const i32) as *mut i32;
}
fn main() {
let mut kekw: *mut i32 = unsafe { 0 as *mut i32 };
{
let mut x = 1337;
let ref_x: &i32 = &x;
kekw = foo(&ref_x);
}
} but its unsafe to make raw pointer zeroed with default trait the only way some one could 'shoot his leg' with zeroed pointer is: pointer = 0;
unsafe { *pointer } which may happen - never but every time I want to use raw pointer in struct, I should do some strange mess with it just give me "std::ptr::SuperUnsafePtr" with zero by default; I will decide what to do in my project by myself with this SuperUnsafePtr PS For everyone with same question 🤓 Added crate for macro: rust_var_zeroed With struct wrapping: pub struct ZeroedMutPtr<T> (pub *mut T);
impl<T> Default for ZeroedMutPtr<T> {
fn default() -> ZeroedMutPtr<T> {
return unsafe {
ZeroedMutPtr (0 as *mut T)
};
}
}
impl<T> ZeroedMutPtr<T> {
fn to_ref(&self) -> &T {
unsafe { &*(self.0) }
}
fn to_mut_ref(&self) -> &T {
unsafe { &*(self.0) }
}
} With macro (C way): #[macro_export]
macro_rules! var_stack_zeroed {
($var_name: ident, $var_type: ty) => {
let mut $var_name: &mut $var_type;
unsafe {
$var_name = &mut *(((&mut ([ 0 as u8; std::mem::size_of::<$var_type>() ])) as *mut [u8; std::mem::size_of::<$var_type>()]) as *mut $var_type)
}
};
}
#[macro_export]
macro_rules! var_heap_zeroed {
($var_name: ident, $var_type: ty) => {
let mut $var_name: *mut $var_type;
unsafe {
let layout = std::alloc::Layout::new::<$var_type>();
$var_name = std::alloc::alloc_zeroed(layout) as *mut $var_type;
}
};
}
struct MyStruct {
a: i32,
ptr: *mut MyStruct,
}
fn main() {
var_stack_zeroed!(my_var, MyStruct);
println!("{:?}", my_var.ptr); // -> 0x0
var_heap_zeroed!(ptr_str, MyStruct);
println!("{}", unsafe { (*ptr_str).a }); // -> 0
} |
the expression |
I've run into the lack of Many C interfaces use the null pointer to denote the equivalent of I just don't understand how implementing In particular, it takes several invariants just to dereference a pointer at all: it must be non-null, writable if you're trying to write to it, not aliased with unique references, not concurrently accessed in a way that would cause a data race, and not already deallocated. If you're ever in a situation where you're trying to dereference a Thus, since null raw pointers have a number of valid use cases in FFI code (to safely construct zeroed objects to be filled, or to indicate the equivalent of |
@phaazon I don't agree with this take. By this logic, the default value of a
In keeping with this theme, it makes sense for a raw pointer to be default initialized as 0 (aka NULL). I would attribute the missing default implementation to incompleteness of the standard library, rather than a safety measure. Additionally, default initializing any value incorrectly can be disastrous. Think of what might happen here: #[derive(Default)]
struct Robot {
enable_ethics: bool, // defaults to `false`!!!
}
This is an excellent point. |
Well, yeah, having a « zero » value is useful… but it’s never useful on its own — i.e. In the case of a default value for an option, I think it should be fine to have another trait, like |
The point of the current Likewise, the most important property of the null pointer isn't that its integer value is zero, but that it does not point to any value. Every other pointer can possibly point to something. That's why so many C APIs use a null pointer to indicate a default value, because if the user specifies a null pointer, there is 0 chance that they were specifying an actual value to be read from or written to. You can have your own gripes about the |
This request has been submitted as an ACP rust-lang/libs-team#571 and has been accepted. The implementation rust-lang/rust#139535 is currently waiting for FCP to finish. It will be insta-stable on v1.88 if no concern. |
I'm going to close this in favour of the ACP/FCP. An issue on the RFC repo is kind of an odd place for libs discussions in any case. I for one wasn't initially aware of it. |
Implement `Default` for raw pointers ACP: rust-lang/libs-team#571 This is instantly stable so we will need an FCP here. Closes rust-lang/rfcs#2464
Implement `Default` for raw pointers ACP: rust-lang/libs-team#571 This is instantly stable so we will need an FCP here. Closes rust-lang/rfcs#2464
Rollup merge of rust-lang#139535 - ChrisDenton:default-ptr, r=tgross35 Implement `Default` for raw pointers ACP: rust-lang/libs-team#571 This is instantly stable so we will need an FCP here. Closes rust-lang/rfcs#2464
Implement `Default` for raw pointers ACP: rust-lang/libs-team#571 This is instantly stable so we will need an FCP here. Closes rust-lang/rfcs#2464
There is little info about this anywhere but is this an oversight?
A default value that makes the pointers point nowhere (like
NULL
) looks ok to me, but maybe I am missing something.The text was updated successfully, but these errors were encountered: