some conversions cleanup
This commit is contained in:
parent
13b2605ed9
commit
94a89e561a
4 changed files with 39 additions and 23 deletions
|
|
@ -1,9 +1,21 @@
|
|||
% Casts
|
||||
|
||||
Casts are a superset of coercions: every coercion can be explicitly invoked via
|
||||
a cast, but some conversions *require* a cast. These "true casts" are generally
|
||||
regarded as dangerous or problematic actions. True casts revolve around raw
|
||||
pointers and the primitive numeric types. True casts aren't checked.
|
||||
Casts are a superset of coercions: every coercion can be explicitly
|
||||
invoked via a cast. However some conversions *require* a cast.
|
||||
While coercions are pervasive and largely harmless, these "true casts"
|
||||
are rare and potentially dangerous. As such, casts must be explicitly invoked
|
||||
using the `as` keyword: `expr as Type`.
|
||||
|
||||
True casts generally revolve around raw pointers and the primitive numeric
|
||||
types. Even though they're dangerous, these casts are *infallible* at runtime.
|
||||
If a cast triggers some subtle corner case no indication will be given that
|
||||
this occurred. The cast will simply succeed.
|
||||
|
||||
That said, casts aren't `unsafe` because they generally can't violate memory
|
||||
safety *on their own*. For instance, converting an integer to a raw pointer can
|
||||
very easily lead to terrible things. However the act of creating the pointer
|
||||
itself is safe, because actually using a raw pointer is already marked as
|
||||
`unsafe`.
|
||||
|
||||
Here's an exhaustive list of all the true casts. For brevity, we will use `*`
|
||||
to denote either a `*const` or `*mut`, and `integer` to denote any integral
|
||||
|
|
@ -22,13 +34,8 @@ primitive:
|
|||
* `fn as *T` where `T: Sized`
|
||||
* `fn as integer`
|
||||
|
||||
where `&.T` and `*T` are references of either mutability,
|
||||
and where unsize_kind(`T`) is the kind of the unsize info
|
||||
in `T` - the vtable for a trait definition (e.g. `fmt::Display` or
|
||||
`Iterator`, not `Iterator<Item=u8>`) or a length (or `()` if `T: Sized`).
|
||||
|
||||
Note that lengths are not adjusted when casting raw slices -
|
||||
`T: *const [u16] as *const [u8]` creates a slice that only includes
|
||||
`*const [u16] as *const [u8]` creates a slice that only includes
|
||||
half of the original memory.
|
||||
|
||||
Casting is not transitive, that is, even if `e as U1 as U2` is a valid
|
||||
|
|
|
|||
|
|
@ -9,8 +9,7 @@ Here's all the kinds of coercion:
|
|||
|
||||
Coercion is allowed between the following types:
|
||||
|
||||
* Subtyping: `T` to `U` if `T` is a [subtype](lifetimes.html#subtyping-and-variance)
|
||||
of `U`
|
||||
* Subtyping: `T` to `U` if `T` is a [subtype][] of `U`
|
||||
* Transitivity: `T_1` to `T_3` where `T_1` coerces to `T_2` and `T_2` coerces to `T_3`
|
||||
* Pointer Weakening:
|
||||
* `&mut T` to `&T`
|
||||
|
|
@ -25,7 +24,6 @@ only implemented automatically, and enables the following transformations:
|
|||
|
||||
* `[T, ..n]` => `[T]`
|
||||
* `T` => `Trait` where `T: Trait`
|
||||
* `SubTrait` => `Trait` where `SubTrait: Trait` (TODO: is this now implied by the previous?)
|
||||
* `Foo<..., T, ...>` => `Foo<..., U, ...>` where:
|
||||
* `T: Unsize<U>`
|
||||
* `Foo` is a struct
|
||||
|
|
@ -70,3 +68,5 @@ fn main() {
|
|||
<anon>:10 foo(t);
|
||||
^~~
|
||||
```
|
||||
|
||||
[subtype]: subtyping.html
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
% Type Conversions
|
||||
|
||||
At the end of the day, everything is just a pile of bits somewhere, and type
|
||||
systems are just there to help us use those bits right. Needing to reinterpret
|
||||
those piles of bits as different types is a common problem and Rust consequently
|
||||
gives you several ways to do that.
|
||||
systems are just there to help us use those bits right. There are two common
|
||||
problems with typing bits: needing to reinterpret those exact bits as a
|
||||
different type, and needing to change the bits to have equivalent meaning for
|
||||
a different type. Because Rust encourages encoding important properties in the
|
||||
type system, these problems are incredibly pervasive. As such, Rust
|
||||
consequently gives you several ways to solve them.
|
||||
|
||||
First we'll look at the ways that *Safe Rust* gives you to reinterpret values.
|
||||
The most trivial way to do this is to just destructure a value into its
|
||||
|
|
@ -26,6 +29,6 @@ fn reinterpret(foo: Foo) -> Bar {
|
|||
}
|
||||
```
|
||||
|
||||
But this is, at best, annoying to do. For common conversions, Rust provides
|
||||
But this is, at best, annoying. For common conversions, Rust provides
|
||||
more ergonomic alternatives.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
% Transmutes
|
||||
|
||||
Get out of our way type system! We're going to reinterpret these bits or die
|
||||
trying! Even though this book is all about doing things that are unsafe, I really
|
||||
can't emphasize that you should deeply think about finding Another Way than the
|
||||
operations covered in this section. This is really, truly, the most horribly
|
||||
unsafe thing you can do in Rust. The railguards here are dental floss.
|
||||
trying! Even though this book is all about doing things that are unsafe, I
|
||||
really can't emphasize that you should deeply think about finding Another Way
|
||||
than the operations covered in this section. This is really, truly, the most
|
||||
horribly unsafe thing you can do in Rust. The railguards here are dental floss.
|
||||
|
||||
`mem::transmute<T, U>` takes a value of type `T` and reinterprets it to have
|
||||
type `U`. The only restriction is that the `T` and `U` are verified to have the
|
||||
|
|
@ -17,8 +17,11 @@ same size. The ways to cause Undefined Behaviour with this are mind boggling.
|
|||
* Making a primitive with an invalid value is UB
|
||||
* Transmuting between non-repr(C) types is UB
|
||||
* Transmuting an & to &mut is UB
|
||||
* Transmuting an & to &mut is *always* UB
|
||||
* No you can't do it
|
||||
* No you're not special
|
||||
* Transmuting to a reference without an explicitly provided lifetime
|
||||
produces an [unbound lifetime](lifetimes.html#unbounded-lifetimes)
|
||||
produces an [unbounded lifetime][]
|
||||
|
||||
`mem::transmute_copy<T, U>` somehow manages to be *even more* wildly unsafe than
|
||||
this. It copies `size_of<U>` bytes out of an `&T` and interprets them as a `U`.
|
||||
|
|
@ -26,4 +29,7 @@ The size check that `mem::transmute` has is gone (as it may be valid to copy
|
|||
out a prefix), though it is Undefined Behaviour for `U` to be larger than `T`.
|
||||
|
||||
Also of course you can get most of the functionality of these functions using
|
||||
pointer casts.
|
||||
pointer casts.
|
||||
|
||||
|
||||
[unbounded lifetime]: unbounded-lifetimes.html
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue