Skip to content

Commit 73bca2b

Browse files
committed
Auto merge of #46094 - dtolnay:is_null, r=alexcrichton
Remove `T: Sized` on `ptr::is_null()` Originally from #44932 -- this is purely a revert of the last commit of that PR, which was removing some changes from the previous commits in the PR. So a revert of a revert means this is code written by @cuviper! @mikeyhew makes a compelling case in rust-lang/rfcs#433 (comment) for why this is the right way to implement `is_null` for trait objects. And the behavior for slices makes sense to me as well. ```diff impl<T: ?Sized> *const T { - pub fn is_null(self) -> bool where T: Sized; + pub fn is_null(self) -> bool; } impl<T: ?Sized> *mut T { - pub fn is_null(self) -> bool where T: Sized; + pub fn is_null(self) -> bool; }
2 parents 71340ca + e0f58c6 commit 73bca2b

File tree

3 files changed

+55
-15
lines changed

3 files changed

+55
-15
lines changed

src/libcore/nonzero.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,7 @@ macro_rules! impl_zeroable_for_pointer_types {
2828
unsafe impl<T: ?Sized> Zeroable for $Ptr {
2929
#[inline]
3030
fn is_zero(&self) -> bool {
31-
// Cast because `is_null` is only available on thin pointers
32-
(*self as *mut u8).is_null()
31+
(*self).is_null()
3332
}
3433
}
3534
)+

src/libcore/ptr.rs

+21-13
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,11 @@ pub unsafe fn write_volatile<T>(dst: *mut T, src: T) {
474474
impl<T: ?Sized> *const T {
475475
/// Returns `true` if the pointer is null.
476476
///
477+
/// Note that unsized types have many possible null pointers, as only the
478+
/// raw data pointer is considered, not their length, vtable, etc.
479+
/// Therefore, two pointers that are null may still not compare equal to
480+
/// each other.
481+
///
477482
/// # Examples
478483
///
479484
/// Basic usage:
@@ -485,8 +490,10 @@ impl<T: ?Sized> *const T {
485490
/// ```
486491
#[stable(feature = "rust1", since = "1.0.0")]
487492
#[inline]
488-
pub fn is_null(self) -> bool where T: Sized {
489-
self == null()
493+
pub fn is_null(self) -> bool {
494+
// Compare via a cast to a thin pointer, so fat pointers are only
495+
// considering their "data" part for null-ness.
496+
(self as *const u8) == null()
490497
}
491498

492499
/// Returns `None` if the pointer is null, or else returns a reference to
@@ -518,9 +525,7 @@ impl<T: ?Sized> *const T {
518525
#[stable(feature = "ptr_as_ref", since = "1.9.0")]
519526
#[inline]
520527
pub unsafe fn as_ref<'a>(self) -> Option<&'a T> {
521-
// Check for null via a cast to a thin pointer, so fat pointers are only
522-
// considering their "data" part for null-ness.
523-
if (self as *const u8).is_null() {
528+
if self.is_null() {
524529
None
525530
} else {
526531
Some(&*self)
@@ -1107,6 +1112,11 @@ impl<T: ?Sized> *const T {
11071112
impl<T: ?Sized> *mut T {
11081113
/// Returns `true` if the pointer is null.
11091114
///
1115+
/// Note that unsized types have many possible null pointers, as only the
1116+
/// raw data pointer is considered, not their length, vtable, etc.
1117+
/// Therefore, two pointers that are null may still not compare equal to
1118+
/// each other.
1119+
///
11101120
/// # Examples
11111121
///
11121122
/// Basic usage:
@@ -1118,8 +1128,10 @@ impl<T: ?Sized> *mut T {
11181128
/// ```
11191129
#[stable(feature = "rust1", since = "1.0.0")]
11201130
#[inline]
1121-
pub fn is_null(self) -> bool where T: Sized {
1122-
self == null_mut()
1131+
pub fn is_null(self) -> bool {
1132+
// Compare via a cast to a thin pointer, so fat pointers are only
1133+
// considering their "data" part for null-ness.
1134+
(self as *mut u8) == null_mut()
11231135
}
11241136

11251137
/// Returns `None` if the pointer is null, or else returns a reference to
@@ -1151,9 +1163,7 @@ impl<T: ?Sized> *mut T {
11511163
#[stable(feature = "ptr_as_ref", since = "1.9.0")]
11521164
#[inline]
11531165
pub unsafe fn as_ref<'a>(self) -> Option<&'a T> {
1154-
// Check for null via a cast to a thin pointer, so fat pointers are only
1155-
// considering their "data" part for null-ness.
1156-
if (self as *const u8).is_null() {
1166+
if self.is_null() {
11571167
None
11581168
} else {
11591169
Some(&*self)
@@ -1277,9 +1287,7 @@ impl<T: ?Sized> *mut T {
12771287
#[stable(feature = "ptr_as_ref", since = "1.9.0")]
12781288
#[inline]
12791289
pub unsafe fn as_mut<'a>(self) -> Option<&'a mut T> {
1280-
// Check for null via a cast to a thin pointer, so fat pointers are only
1281-
// considering their "data" part for null-ness.
1282-
if (self as *mut u8).is_null() {
1290+
if self.is_null() {
12831291
None
12841292
} else {
12851293
Some(&mut *self)

src/libcore/tests/ptr.rs

+33
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,39 @@ fn test_is_null() {
6262

6363
let mq = unsafe { mp.offset(1) };
6464
assert!(!mq.is_null());
65+
66+
// Pointers to unsized types -- slices
67+
let s: &mut [u8] = &mut [1, 2, 3];
68+
let cs: *const [u8] = s;
69+
assert!(!cs.is_null());
70+
71+
let ms: *mut [u8] = s;
72+
assert!(!ms.is_null());
73+
74+
let cz: *const [u8] = &[];
75+
assert!(!cz.is_null());
76+
77+
let mz: *mut [u8] = &mut [];
78+
assert!(!mz.is_null());
79+
80+
let ncs: *const [u8] = null::<[u8; 3]>();
81+
assert!(ncs.is_null());
82+
83+
let nms: *mut [u8] = null_mut::<[u8; 3]>();
84+
assert!(nms.is_null());
85+
86+
// Pointers to unsized types -- trait objects
87+
let ci: *const ToString = &3;
88+
assert!(!ci.is_null());
89+
90+
let mi: *mut ToString = &mut 3;
91+
assert!(!mi.is_null());
92+
93+
let nci: *const ToString = null::<isize>();
94+
assert!(nci.is_null());
95+
96+
let nmi: *mut ToString = null_mut::<isize>();
97+
assert!(nmi.is_null());
6598
}
6699

67100
#[test]

0 commit comments

Comments
 (0)