Impl convert::From for pointer<->usize conversions

Casting via the as operator doesn't force the programmer to deal with the possibility that a value wasn't perfectly converted into the other type; as indicates that the conversion is potentially lossy.

It is possible to cast a pointer via the as operator to any integral type, e.g. u8, which actually is lossy, and furthermore, likely unintended. To clearly convey that the conversion shouldn't fail, I'd like to see usizs::from(raw_pointer).

Would it make sense to implement these four traits in the stdlib?

impl<T: ?Sized> std::convert::From<*const T> for usize;

impl<T: ?Sized> std::convert::From<usize> for *const T;

impl<T: ?Sized> std::convert::From<*mut T> for usize;

impl<T: ?Sized> std::convert::From<usize> for *mut T;
  • Nothing prevents you from having lossy implementation of From, they just need to be infallible
  • The conversion pointer -> usize is actually lossy anyway because you lose provenance.

Aren't these lossy too? You're losing the pointer metadata if T is not Sized (excluding extern types)

Again, if T is not Sized (excluding extern types) you won't be able to generate the metadata part of the pointer (which by the way it's still unclear whether it has to be valid even for raw pointers or not)

1 Like

I thought that integers had provenance as well..

If you lose provenance, how is it allowed to convert usize to a pointer and actually dereference it?

As I understand it when you convert a pointer to an integer, the address is marked as leaked. When you convert an integer back to a pointer and then use it, you are only allowed to access memory associated with leaked addresses or memory outside of the control of the abstract machine like MMIO.

I'd really rather that From::from(4) couldn't ever give me a pointer. It feels different enough in "kind" (for a vague definition of kind) that I'd rather it go through something with a specific name. For example, if one is calling From::from(&8_usize) it seems very unlikely that they wanted the "coerce to pointer and get usize" version.

On lang we were discussing a few months back about wanting specific methods like that for other reasons anyway. (See https://rust-lang.zulipchat.com/#narrow/stream/219381-t-libs/topic/Adding.20methods.20as.20more.20specific.20versions.20of.20.60as.60/near/238379738 or Adding methods as more specific versions of `as` · t-libs · Zulip Chat Archive) Notably, when pointers are involved, because able to lint foo as u8 when foo is a pointer would be nice so that we can say "did you mean foo() as u8 or (*foo) as u8 or foo.to_bits() as u8?"

5 Likes

Yes, I agree with this more than my own suggestion. It seems very different for converting pointers than converting numbers between each other. A means of explicitly, correctly, casting between pointers is all that I'd like to see, whatever form it takes.

Well, sure, but I'm thinking in terms of integral/floating casts between each other :wink:

Oh, my bad, I knew about this prior to the quick writeup, but had forgotten about it when writing this! If a *const T can be casted to usize via an as conversion, then I'm suggesting that <*const T>::from(usize) should be possible, of course, the only time that, that is possible is when T: Sized :sweat_smile:

Fully agreed. Like how there's cast for *mut T -> *mut U and *const T -> *const U, to help avoid accidentally doing *const T -> *mut U.