Skip to content

Add some documentation about what as casts are legal #30088

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Nov 29, 2015
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 113 additions & 5 deletions src/doc/book/casting-between-types.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,127 @@ different types between each other. The first, `as`, is for safe casts.
In contrast, `transmute` allows for arbitrary casting, and is one of the
most dangerous features of Rust!

# Coercion

Coercion between types is implicit and has no syntax of its own, but can
be spelled out with [`as`](#explicit-coercions).

Coercion occurs in `let`, `const`, and `static` statements; in
function call arguments; in field values in struct initialization; and in a
function result.

The main cases of coercion are:

* `&mut T` to `&T`

* `*mut T` to `*const T`

* `&T` to `*const T`

* `&mut T` to `*mut T`

* A custom coercion using [`Deref`](deref-coercions.md)


# `as`

The `as` keyword does basic casting:
The `as` keyword does safe casting:

```rust
let x: i32 = 5;

let y = x as i64;
```

It only allows certain kinds of casting, however:
There are three major categories of safe cast: explicit coercions, casts
between numeric types, and pointer casts.

Casting is not transitive: even if `e as U1 as U2` is a valid
expression, `e as U2` is not necessarily so (in fact it will only be valid if
`U1` coerces to `U2`).


## Explicit coercions

A cast `e as U` is valid if `e` has type `T` and `T` *coerces* to `U`.

For example:

```rust
let a = "hello";
let b = a as String;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... this doesn't compile.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:/

I'll send a follow-up

```

## Numeric casts

A cast `e as U` is also valid in any of the following cases:

* `e` has type `T` and `T` and `U` are any numeric types; *numeric-cast*
* `e` is a C-like enum (with no data attached to the variants),
and `U` is an integer type; *enum-cast*
* `e` has type `bool` or `char` and `U` is an integer type; *prim-int-cast*
* `e` has type `u8` and `U` is `char`; *u8-char-cast*

For example

```rust
let one = true as u8;
let at_sign = 64 as char;
```

The semantics of numeric casts are:

* Casting between two integers of the same size (e.g. i32 -> u32) is a no-op
* Casting from a larger integer to a smaller integer (e.g. u32 -> u8) will
truncate
* Casting from a smaller integer to a larger integer (e.g. u8 -> u32) will
* zero-extend if the source is unsigned
* sign-extend if the source is signed
* Casting from a float to an integer will round the float towards zero
* **[NOTE: currently this will cause Undefined Behavior if the rounded
value cannot be represented by the target integer type][float-int]**.
This includes Inf and NaN. This is a bug and will be fixed.
* Casting from an integer to float will produce the floating point
representation of the integer, rounded if necessary (rounding strategy
unspecified)
* Casting from an f32 to an f64 is perfect and lossless
* Casting from an f64 to an f32 will produce the closest possible value
(rounding strategy unspecified)
* **[NOTE: currently this will cause Undefined Behavior if the value
is finite but larger or smaller than the largest or smallest finite
value representable by f32][float-float]**. This is a bug and will
be fixed.

[float-int]: https://github.com/rust-lang/rust/issues/10184
[float-float]: https://github.com/rust-lang/rust/issues/15536

## Pointer casts

Perhaps surprisingly, it is safe to cast pointers to and from integers, and
to cast between pointers to different types subject to some constraints. It
is only unsafe to dereference the pointer.

`e as U` is a valid pointer cast in any of the following cases:

* `e` has type `*T`, `U` has type `*U_0`, and either `U_0: Sized` or
`unsize_kind(T) == unsize_kind(U_0)`; a *ptr-ptr-cast*

* `e` has type `*T` and `U` is a numeric type, while `T: Sized`; *ptr-addr-cast*

* `e` is an integer and `U` is `*U_0`, while `U_0: Sized`; *addr-ptr-cast*

* `e` has type `&[T; n]` and `U` is `*const T`; *array-ptr-cast*

* `e` is a function pointer type and `U` has type `*T`,
while `T: Sized`; *fptr-ptr-cast*

* `e` is a function pointer type and `U` is an integer; *fptr-addr-cast*


# `transmute`

`as` only allows safe casting, and will for example reject an attempt to
cast four bytes into a `u32`:

```rust,ignore
let a = [0u8, 0u8, 0u8, 0u8];
Expand All @@ -31,13 +141,11 @@ let b = a as u32; // four eights makes 32
^~~~~~~~
```

It’s a ‘non-scalar cast’ because we have multiple values here: the four
This is a ‘non-scalar cast’ because we have multiple values here: the four
elements of the array. These kinds of casts are very dangerous, because they
make assumptions about the way that multiple underlying structures are
implemented. For this, we need something more dangerous.

# `transmute`

The `transmute` function is provided by a [compiler intrinsic][intrinsics], and
what it does is very simple, but very scary. It tells Rust to treat a value of
one type as though it were another type. It does this regardless of the
Expand Down