c2rust
c2rust copied to clipboard
Use `std::ptr::null_mut::<T>()` instead of `0 as *mut T`
c2rust current transpiles NULL pointers as 0 as *mut T, but it would be a bit cleaner and more rust-y to instead produce std::ptr::null_mut::<T>().
This keeps coming up in our internal discussion occasionally, but there are some corner cases where they're not equivalent (I think in static initializers, I have to look up the original discussion for details). Meanwhile, you can do the following with c2rust-refactor:
rewrite_expr '0 as *const $t:Ty' 'std::ptr::null()' ;
rewrite_expr '0 as *mut $t:Ty' 'std::ptr::null_mut()' ;
I also recall the two not being equivalent. I wonder if it was just because it wasn't a const fn at the time? Maybe it'd be viable now, but I'm not certain
I checked last night, null and null_mut have been const fns since 2015.
Hmm. Guess it wasn't that, lol
One reason why they wouldn't be equivalent is static promotion. 0 as *mut T can be directly promoted to consts and statics. On the other hand, ptr::null_mut is a const fn, and const fn calls in general cannot be safely promoted. The specific functions ptr::null and ptr::null_mut are rustc_promotable, but this may change in the future and is harder to track for the refactoring tools. I think it's unlikely that the transpiled code will depend on static promotion (it's more of a syntactic sugar), but avoiding such code is still reasonable.
Also, being function calls they may be harder in general to analyze. Knowing that they are really null requires either interprocedural analysis (which may be intractable in general) or special-casing.
Finally, these function calls shouldn't even normally appear in idiomatic Rust code. They are generally an artifact of partial initialization, redundant default-initialization (which should be folded away) or nullable pointers (which are better expressed as Option<&T> or Option<NotNull<T>>). For this reason optimizing for their readability seems counterproductive, they should be optimized for easy elimination.
I think this should be re-considered, as 0 as *const T and null() are no longer equivalent under strict provenance. We should prefer null() and null_mut() where possible to avoid the int-to-ptr casts. For example, miri warns about this, and can't catch all UB under permissive provenance. Where we do need an int-to-ptr cast, we should use the ptr::from_exposed_addr API to make this explicit, not just an as cast.
FYI, this also makes clippy complain when run on a transpiled codebase.