Transmutation changes something over into something else. Thus, a writer may transmute his life into stories or novels, and an arranger might transmute a lively march tune into a quiet lullaby. In the "Myth of Er" at the end of Plato's Republic, for example, human souls are transmuted into the body and existence of their choice. Having learned from their last life what they do not want to be, many choose transmutation into something that seems better. A meek man chooses to be transmuted into a tyrant, a farmer into a dashing (but short-lived) warrior, and so on. But very few seem to have learned anything from their former life that would make their choice a real improvement.
I understand that pointer casts cannot cause any problems without unsafe code, but to me they seem like a delayed transmute happens to be possible in safe code, even though transmutes are "the most horribly unsafe thing you can do in Rust".
I guess the problem in this program is that wrong_ptr and x alias and have different understandings of the constraints that the pointed to data has.
Maybe something like that should be added to the safety documentation of std::ptr::write?
I mean, you can transmute it instead of casting it and have the same issue. Transmute is considered more dangerous because it doesn't verify that you are actually transmuting between two pointer types.
This "extra information" attached to the pointer is folded into the "provenance" of the pointer. The actual act of writing 0 through the pointer is not UB itself, it's the writing 0 to a memory location typed at NonZeroI32 that is the UB.
For example, if the memory location were Option, it would be perfectly legal to write 0 into a pointer to that memory slot, even though nothing is different about the pointer's bits. (Whether writing 0 to the NonZeroI32 from inside the option is UB is an open question; I say UB, and that's the safer assumption ofc.)
Also of note is that this is only instant UB because the written memory is a typed stack slot. If it were a heap memory location, then it's only UB once the value is accessed at a type the bits are no longer valid for*.
* Well, it's an open question whether references have a validity requirement for their target pointee to also be valid, but the UWG is leaning towards it being a safety invariant on references (that's active even on things like a typed copy of the reference).
How would someone who just looked at the safety documentation of the only unsafe function used in this example find this out then?
Should there be a "dst must not have non-local properties which are being violated by this write" condition in the safety documentation of std::ptr::write and similar functions?
In this example (assuming the println statement didn't exist) x is not assigned, passed to a function/primitive operation or returned from a function/primitive operation after the line that caused the UB, so [2] cannot refer to x.
Pointer casts when combined with ptr::read/write or deref are capable of doing everything that transmute can do, so in this sense they are as powerful (and as dangerous) as transmute. For example, you can implement transmute (except for the static size check) using a pointer cast plus a read:
However, this is really a consequence of the basic property of raw pointers: The compiler does not check or guarantee their validity in any way, and therefore anything you do with them is incredibly unsafe. For example, raw pointers also allow you to implement transmute without any pointer casts!
I guess what confuses/worries me is that the transmute documentation states its "incredibly unsafe" and should be the "the absolute last resort", whereas the same things are possible with ptr::read/write (potentially even introducing non-locality, where transmute would have local effects) and the documentation of these is fairly short and benign in its wording.
I guess what confuses/worries me is that the transmute documentation states its " incredibly unsafe" ... whereas ... the documentation of [ptr::read/write] is fairly short and benign in its wording.
This is a case where there's a disagreement between informal interpretation of unsafe as "tricky code, pay attention!" and Rust's formal definition of unsafety focused on UB and UB-causing loopholes.
And back to the topic at hand with this manually hand-rolled transmute (or one using unions): indeed, casts and unions can be more error-prone than direct usage of transmute, since the latter requires that T and U be equal sized, which neither as pointer casts nor unions do.
What I meant was that in pretty much any real world program where a pointer cast is performed, that is done with the intention of actually using the pointer.
And the pointer cast is what enables misuse of the pointer in most situations, arguably in potentially worse ways than a transmute, because of the non-locality that is introduced with pointer casts.
Pointer casts are nonexistent, because there are no types at the binary level. Type safety is a property of programming languages and casting a value to another type is simply a way to make the compiler believe, that the value its looking at is something different. The only way to cause undefined behavior is by interacting with a value in a way, that it doesn't support. Due to aggressive compiler optimization, just because you don't explicitly interact with a value doesn't mean, that the compiler may not insert code by itself that interacts with the value, that causes UB. Having 2 or more mutable borrows (&mut) to the same value is one of those examples, that is instantly UB due to potential compiler optimizations.
AFAIK, LLVM doesn't perform any magic regarding &mut, yet, but it is supposed to be doing that in the future, i.e. at the moment you would most likely not notice your program behaving in a way you don't expect, but compiling the same program in the future with a newer underlying version of LLVM may suddenly cause a different behavior than it did previously.
Due to aggressive compiler optimization, just because you don't explicitly interact with a value doesn't mean, that the compiler may not insert code by itself that interacts with the value, that causes UB.
What I meant was that in pretty much any real world program where a pointer cast is performed, that is done with the intention of actually using the pointer.
And the pointer cast is what enables misuse of the pointer in most situations, arguably in potentially worse ways than a transmute , because of the non-locality that is introduced with pointer casts.
The trick with "with the intention" is that it can make almost everything dangerous. It would be possible to argue that, for example, that it's the subtraction that introduces the problem in something like a.get_unchecked(i-1), but we also wouldn't use that to say that subtraction needs to be unsafe.
I've been backed into a corner where I need to transmute a boxed trait object to the correct trait type. I know for certain that in this match arm the boxed item is this trait object, I'm just transmuting it because the type can't be written in the signature, yet transmute fails:
I understand boxes can be one of two sizes (depending on whether they point to a struct or a trait object), but have no idea how to express this in the T type constraint. This fn will only ever be taking boxed trait objects.
By the way, I'm almost 100% certain that this will invoke undefined behavior in your code, because the two types actually DO differ in sizedness. (I've used such code to go from unsized to unsized, and from sized to sized, but you're going from unsized to sized!)
Unless I've vastly misunderstand Any (having used it elsewhere), I can't use it here because I can't write/don't want the concrete type (ThingThatImplsRelevantTrait) of the thing that was originally boxed, just the trait it was boxed as before the fn was called (RelevantTrait).
Transmute doesn't do conversions, per se. The output is identical to the input, it just has a different type. The compiler is saving you here -- your Box does not have a representation that is compatible with the type you are trying to transmute into.
What you need is a conversion, something that creates a value with the right representation, the right data in that representation, and then possibly transmute can help you make it into a value that the compiler thinks is the desired type.
A library (let's call it 'Base') defines a set of types that all have trait A. These types must be heterogeneous because they behave differently. Some of these types may also have traits B, C, D, etc (or are able to create other types that do). These types have relations to each other so the library has structs, functions, etc. that store and perform computation on those relations. Whatever.
A library using/extending this library also may define some of these types implementing A. These externally defined types may also have traits B, C, D, as well as M, N, O, etc. However it still wants to use the relational storage and computation from Base.
To handle this much of Base is generic on macro-generated enum types that are created by the user library. However most of Base is still interacting with things as As. When you need to get, e.g., an M interface, it's easy to ask the type via A if it impls M, and it's easy for the type to create a Box, but for A to return things that may be Box where T in A, B, ..., M, N etc. requires another macro-generated enum user type X (several, actually), which, again, much of Base is generic over. To get a Box out of this type, the enum has to unpack itself. So, a type has to create the X::M(Box), but can't write X::M because there's no..."enum trait"(?)..in Rust, but it knows for sure it needs to create a Box and X knows for sure it can construct a variant around a Box. But the trait Y which X is generic over in 'Base' can't write M or really any definite trait types, so it just says "things that impl me will know if they have a variant for a Box, and will know for sure what T is by an identifier" (which I substituted with a &str in the example for clarity).
c80f0f1006