Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.
Sign upRemove `T: Sized` on pointer `as_ref()` and `as_mut()` #44932
Conversation
rust-highfive
assigned
alexcrichton
Sep 29, 2017
This comment has been minimized.
This comment has been minimized.
|
(rust_highfive has picked a reviewer for you, use r? to override) |
bluss
reviewed
Sep 29, 2017
| assert!(!mz.is_null()); | ||
|
|
||
| unsafe { | ||
| let ncs: *const [u8] = slice::from_raw_parts(null(), 0); |
This comment has been minimized.
This comment has been minimized.
bluss
Sep 29, 2017
Contributor
This is UB (null in a &[u8]) so unfortunately can begin to fail when the compiler gets smarter.
This comment has been minimized.
This comment has been minimized.
cuviper
Sep 29, 2017
Author
Member
Is there any better way to get a null unsized pointer? Or are they impossible to be null in practice? If the latter, we could just drop those parts of the tests.
This comment has been minimized.
This comment has been minimized.
cuviper
Sep 29, 2017
Author
Member
I suppose I could implement a local Repr hack for the tests, similar to slice::from_raw_parts but without ever calling it a &.
This comment has been minimized.
This comment has been minimized.
cramertj
Sep 29, 2017
Member
Depending on how much you hate transmute, you could do let ncs: *const [u8] = unsafe { mem::transmute((0usize, 0usize)) };
This comment has been minimized.
This comment has been minimized.
cuviper
Sep 29, 2017
•
Author
Member
I went ahead and added a transmute with Repr, which I think is a little safer than a tuple because it can be #[repr(C)]. This is just for testing anyway, so whatever we do isn't going to affect anyone in the wild.
cuviper
added some commits
Sep 29, 2017
This comment has been minimized.
This comment has been minimized.
|
@bluss also pointed out that it could be surprising that there are many possible null values for fat pointers. I added docs mentioning this, but if that's too weird, we could back off to just unsized |
This comment has been minimized.
This comment has been minimized.
|
To do the null pointer right (via @eddyb): |
This comment has been minimized.
This comment has been minimized.
|
Looks great to me! Since this'll be instantly stable, let's.... @rfcbot fcp merge |
alexcrichton
added
the
T-libs
label
Sep 30, 2017
rfcbot
added
the
proposed-final-comment-period
label
Sep 30, 2017
This comment has been minimized.
This comment has been minimized.
rfcbot
commented
Sep 30, 2017
•
|
Team member @alexcrichton has proposed to merge this. The next step is review by the rest of the tagged teams: Concerns:
Once these reviewers reach consensus, this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up! See this document for info about what commands tagged team members can give me. |
eddyb
reviewed
Sep 30, 2017
| pub len: usize, | ||
| } | ||
|
|
||
| mem::transmute(Repr { data: null_mut::<T>(), len: 0 }) |
This comment has been minimized.
This comment has been minimized.
eddyb
Sep 30, 2017
•
Member
You don't need this: you can just cast null_mut::<[T; 0]>() to *mut [T].
EDIT: ah yes I see that @bluss commented about this already.
This comment has been minimized.
This comment has been minimized.
|
OK, it now uses coercion to get the null pointers, and I added trait objects this way too. |
This comment has been minimized.
This comment has been minimized.
|
This is not the behavior that Go has for comparing interfaces (read: traits) against nil (read: null). Are we sure the behavior in this PR is useful? How does Go justify their behavior and do they regret it? The package main
import "fmt"
type S struct {}
func (*S) String() string {
return "S"
}
func main() {
var s fmt.Stringer = (*S)(nil)
fmt.Println(s == nil) // false
}@rfcbot concern go |
This comment has been minimized.
This comment has been minimized.
|
@dtolnay Currently it is UB to have a null vtable pointer, it's as if |
This comment has been minimized.
This comment has been minimized.
|
@dtolnay Note however that this isn't changing I don't really know Go. If you have such a |
carols10cents
added
the
S-waiting-on-team
label
Oct 2, 2017
This comment has been minimized.
This comment has been minimized.
|
Re: @cuviper
By design, it is not easy to tell if the data pointer is nil. This can be confusing but it is because interfaces with a nil data pointer are totally fine to call interface methods on -- as long as those methods do not lead to the nil pointer being dereferenced. In the code above, calling If checking the data pointer for nil is really what you mean, there are some ways. But as I understand it, this would be very unusual in Go code (which is why I was startled to see us define // use type assertion to check for nil data pointer of a particular type
p, ok := s.(*S)
if ok {
isNil = p == nil
}
// use reflection to check for nil data pointer of any type
isNil = reflect.ValueOf(s).IsNil()Re: @eddyb
Do you have any sense of what the future holds here? In particular, I would be concerned about how this PR interacts with the following two potential changes:
In combination, these would put us in a state like Go where checking for null data pointer is almost never what you want and checking for null data pointer + null vtable is almost always what you want. Have there been any discussions that would rule out one or both of these from being pursued in the future? |
This comment has been minimized.
This comment has been minimized.
|
If Rust But even with the possibility of Slices are weird too. If we want to defer a decision on |
This comment has been minimized.
This comment has been minimized.
I think that can't happen.
I don't yet agree that this would be useful. Do you have a use case in mind where the best solution involves checking whether the data pointer of a trait object is null? It is possible that these are universally better served by some combination of not using trait objects, using |
This comment has been minimized.
This comment has been minimized.
|
If nothing else, I think |
This comment has been minimized.
This comment has been minimized.
|
I don't think we'll ever see distinct requirements for validity of metadata between safe and unsafe pointers if we get any kind of Custom DSTs, because of the high complexity associated with having it not be uniform and requiring potentially user-defined conversions for casts which are builtin now. As for |
This comment has been minimized.
This comment has been minimized.
This is what makes Go's choice of behavior for Transliterated into Rust it would be: struct S {}
impl Trait for S {
unsafe fn f(*const self) -> &'static str {
"S"
}
}
fn main() {
let s: *const Trait = ptr::null::<S>();
println!("{}", s.is_null()); // false, calling Trait::f is fine
let s: *const Trait = ptr::null::<Trait>();
println!("{}", s.is_null()); // true, calling Trait::f is not fine
} |
This comment has been minimized.
This comment has been minimized.
https://golang.org/doc/faq#nil_error Considering advice like "It's a good idea for functions that return errors always to use the error type in their signature (as we did above) rather than a concrete type such as *MyError, to help guarantee the error is created correctly.", I can imagine this being a design mistake. As for why it happens, it's because there is no explicit p == nilThe types of comparison have to be identical, so an implicit cast is inserted p == interface{}(nil)An interface is pair of a type pointer and a value. As Essentially this makes it a comparison like the following (this is pseudo-Go). (*int, nil) == (nil, nil)
A type needs to be compared while comparing 2 interfaces, as Go does provide value types. For instance, the following program returns package main
import "fmt"
func main() {
fmt.Println(interface{}(int(2)) == interface{}(int(2)))
fmt.Println(interface{}(int(2)) == interface{}(uint(2)))
}Considering Rust doesn't allow To be precise, |
This comment has been minimized.
This comment has been minimized.
|
@xfix we agree that the |
This comment has been minimized.
This comment has been minimized.
|
I pushed an update to restore |
This comment has been minimized.
This comment has been minimized.
|
@ExpHP this PR affects ?Sized types where it is not so clear-cut. In the code from #44932 (comment) I would expect as_ref to return Some in the first case and None in the second case, even though both data pointers are null. With something like custom DSTs from rust-lang/rfcs#1524 things get even stranger. Basically there is a discussion around raw pointer ergonomics that hasn't happened yet and I don't feel comfortable painting ourselves in a corner that we know (from Go) does not necessarily make sense. I would prefer to better understand the design space around raw pointers before making decisions about the semantics, which is tracked in part in rust-lang/rfcs#433. |
This comment has been minimized.
This comment has been minimized.
|
My comment concerns the semantics not of pointers, but of borrows. I am saying that, because
|
This comment has been minimized.
This comment has been minimized.
|
@ExpHP Yes, it seems necessary to produce |
This comment has been minimized.
This comment has been minimized.
|
@ExpHP @bluss in #44932 (comment) I would expect the first case to produce Some with a null data pointer and a non-null vtable. By Go's logic, the fact that the caller passed a non-null pointer EDIT: disregard, this would not make sense |
This comment has been minimized.
This comment has been minimized.
|
@dtolnay so if I understand your responses correctly, you want the following to be on the table as well? (i.e. allow not just raw pointers, but also borrows to have null data): struct S {}
impl Trait for S {
// Notice: &self, not *const self
fn f(&self) -> &'static str {
"S"
}
}
fn main() {
// Notice: &Trait, not a raw pointer
let s: &'static Trait = unsafe { ptr::null::<S>().as_ref().unwrap() };
println!("{}", s.f());
} |
This comment has been minimized.
This comment has been minimized.
|
@ExpHP that code is stable already so changing it would not be on the table. I read your comments again and I find the point about @rfcbot fcp merge |
This comment has been minimized.
This comment has been minimized.
rfcbot
commented
Nov 2, 2017
•
|
Team member @dtolnay has proposed to merge this. The next step is review by the rest of the tagged teams: No concerns currently listed. Once these reviewers reach consensus, this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up! See this document for info about what commands tagged team members can give me. |
rfcbot
added
the
proposed-final-comment-period
label
Nov 2, 2017
This comment has been minimized.
This comment has been minimized.
|
@BurntSushi ticky box here waiting for you! |
This comment has been minimized.
This comment has been minimized.
rfcbot
commented
Nov 7, 2017
|
|
rfcbot
added
final-comment-period
and removed
proposed-final-comment-period
labels
Nov 7, 2017
kennytm
added
S-waiting-on-review
and removed
S-waiting-on-team
labels
Nov 7, 2017
This comment has been minimized.
This comment has been minimized.
|
@bors: r+ |
alexcrichton
closed this
Nov 7, 2017
This comment has been minimized.
This comment has been minimized.
|
|
alexcrichton
reopened this
Nov 7, 2017
This comment has been minimized.
This comment has been minimized.
|
@bors: r+ oops... |
This comment has been minimized.
This comment has been minimized.
|
This comment has been minimized.
This comment has been minimized.
|
|
bors
added a commit
that referenced
this pull request
Nov 7, 2017
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
|
cuviper commentedSep 29, 2017
•
edited
NonZero::is_zero()was already casting all pointers to thin*mut u8to check for null. The same test on unsized fat pointers can also be used withas_ref()andas_mut()to get fat references.(This PR formerly changed
is_null()too, but checking just the data pointer is not obviously correct for trait objects, especially if*const selfsorts of methods are ever allowed.)