]>
Commit | Line | Data |
---|---|---|
92a42be0 SL |
1 | % Casting Between Types |
2 | ||
3 | Rust, with its focus on safety, provides two different ways of casting | |
4 | different types between each other. The first, `as`, is for safe casts. | |
5 | In contrast, `transmute` allows for arbitrary casting, and is one of the | |
6 | most dangerous features of Rust! | |
7 | ||
8 | # Coercion | |
9 | ||
10 | Coercion between types is implicit and has no syntax of its own, but can | |
11 | be spelled out with [`as`](#explicit-coercions). | |
12 | ||
13 | Coercion occurs in `let`, `const`, and `static` statements; in | |
14 | function call arguments; in field values in struct initialization; and in a | |
15 | function result. | |
16 | ||
17 | The most common case of coercion is removing mutability from a reference: | |
18 | ||
19 | * `&mut T` to `&T` | |
54a0048b | 20 | |
92a42be0 SL |
21 | An analogous conversion is to remove mutability from a |
22 | [raw pointer](raw-pointers.md): | |
23 | ||
24 | * `*mut T` to `*const T` | |
54a0048b | 25 | |
92a42be0 SL |
26 | References can also be coerced to raw pointers: |
27 | ||
28 | * `&T` to `*const T` | |
29 | ||
30 | * `&mut T` to `*mut T` | |
31 | ||
32 | Custom coercions may be defined using [`Deref`](deref-coercions.md). | |
33 | ||
34 | Coercion is transitive. | |
54a0048b | 35 | |
92a42be0 SL |
36 | # `as` |
37 | ||
38 | The `as` keyword does safe casting: | |
39 | ||
40 | ```rust | |
41 | let x: i32 = 5; | |
42 | ||
43 | let y = x as i64; | |
44 | ``` | |
45 | ||
46 | There are three major categories of safe cast: explicit coercions, casts | |
47 | between numeric types, and pointer casts. | |
48 | ||
49 | Casting is not transitive: even if `e as U1 as U2` is a valid | |
50 | expression, `e as U2` is not necessarily so (in fact it will only be valid if | |
51 | `U1` coerces to `U2`). | |
52 | ||
53 | ||
54 | ## Explicit coercions | |
55 | ||
56 | A cast `e as U` is valid if `e` has type `T` and `T` *coerces* to `U`. | |
57 | ||
58 | ## Numeric casts | |
59 | ||
60 | A cast `e as U` is also valid in any of the following cases: | |
61 | ||
62 | * `e` has type `T` and `T` and `U` are any numeric types; *numeric-cast* | |
63 | * `e` is a C-like enum (with no data attached to the variants), | |
64 | and `U` is an integer type; *enum-cast* | |
65 | * `e` has type `bool` or `char` and `U` is an integer type; *prim-int-cast* | |
66 | * `e` has type `u8` and `U` is `char`; *u8-char-cast* | |
54a0048b | 67 | |
92a42be0 SL |
68 | For example |
69 | ||
70 | ```rust | |
71 | let one = true as u8; | |
72 | let at_sign = 64 as char; | |
73 | let two_hundred = -56i8 as u8; | |
74 | ``` | |
75 | ||
76 | The semantics of numeric casts are: | |
77 | ||
78 | * Casting between two integers of the same size (e.g. i32 -> u32) is a no-op | |
79 | * Casting from a larger integer to a smaller integer (e.g. u32 -> u8) will | |
80 | truncate | |
81 | * Casting from a smaller integer to a larger integer (e.g. u8 -> u32) will | |
82 | * zero-extend if the source is unsigned | |
83 | * sign-extend if the source is signed | |
84 | * Casting from a float to an integer will round the float towards zero | |
85 | * **[NOTE: currently this will cause Undefined Behavior if the rounded | |
86 | value cannot be represented by the target integer type][float-int]**. | |
87 | This includes Inf and NaN. This is a bug and will be fixed. | |
88 | * Casting from an integer to float will produce the floating point | |
89 | representation of the integer, rounded if necessary (rounding strategy | |
90 | unspecified) | |
91 | * Casting from an f32 to an f64 is perfect and lossless | |
92 | * Casting from an f64 to an f32 will produce the closest possible value | |
93 | (rounding strategy unspecified) | |
94 | * **[NOTE: currently this will cause Undefined Behavior if the value | |
95 | is finite but larger or smaller than the largest or smallest finite | |
96 | value representable by f32][float-float]**. This is a bug and will | |
97 | be fixed. | |
98 | ||
99 | [float-int]: https://github.com/rust-lang/rust/issues/10184 | |
100 | [float-float]: https://github.com/rust-lang/rust/issues/15536 | |
54a0048b | 101 | |
92a42be0 | 102 | ## Pointer casts |
54a0048b | 103 | |
92a42be0 SL |
104 | Perhaps surprisingly, it is safe to cast [raw pointers](raw-pointers.md) to and |
105 | from integers, and to cast between pointers to different types subject to | |
106 | some constraints. It is only unsafe to dereference the pointer: | |
107 | ||
108 | ```rust | |
109 | let a = 300 as *const char; // a pointer to location 300 | |
110 | let b = a as u32; | |
111 | ``` | |
112 | ||
113 | `e as U` is a valid pointer cast in any of the following cases: | |
114 | ||
115 | * `e` has type `*T`, `U` has type `*U_0`, and either `U_0: Sized` or | |
116 | `unsize_kind(T) == unsize_kind(U_0)`; a *ptr-ptr-cast* | |
54a0048b | 117 | |
92a42be0 SL |
118 | * `e` has type `*T` and `U` is a numeric type, while `T: Sized`; *ptr-addr-cast* |
119 | ||
120 | * `e` is an integer and `U` is `*U_0`, while `U_0: Sized`; *addr-ptr-cast* | |
121 | ||
122 | * `e` has type `&[T; n]` and `U` is `*const T`; *array-ptr-cast* | |
123 | ||
124 | * `e` is a function pointer type and `U` has type `*T`, | |
125 | while `T: Sized`; *fptr-ptr-cast* | |
126 | ||
127 | * `e` is a function pointer type and `U` is an integer; *fptr-addr-cast* | |
128 | ||
129 | ||
130 | # `transmute` | |
131 | ||
132 | `as` only allows safe casting, and will for example reject an attempt to | |
133 | cast four bytes into a `u32`: | |
134 | ||
135 | ```rust,ignore | |
136 | let a = [0u8, 0u8, 0u8, 0u8]; | |
137 | ||
138 | let b = a as u32; // four eights makes 32 | |
139 | ``` | |
140 | ||
141 | This errors with: | |
142 | ||
143 | ```text | |
144 | error: non-scalar cast: `[u8; 4]` as `u32` | |
145 | let b = a as u32; // four eights makes 32 | |
146 | ^~~~~~~~ | |
147 | ``` | |
148 | ||
149 | This is a ‘non-scalar cast’ because we have multiple values here: the four | |
150 | elements of the array. These kinds of casts are very dangerous, because they | |
151 | make assumptions about the way that multiple underlying structures are | |
152 | implemented. For this, we need something more dangerous. | |
153 | ||
154 | The `transmute` function is provided by a [compiler intrinsic][intrinsics], and | |
155 | what it does is very simple, but very scary. It tells Rust to treat a value of | |
156 | one type as though it were another type. It does this regardless of the | |
9cc50fc6 | 157 | typechecking system, and completely trusts you. |
92a42be0 SL |
158 | |
159 | [intrinsics]: intrinsics.html | |
160 | ||
161 | In our previous example, we know that an array of four `u8`s represents a `u32` | |
162 | properly, and so we want to do the cast. Using `transmute` instead of `as`, | |
163 | Rust lets us: | |
164 | ||
165 | ```rust | |
166 | use std::mem; | |
167 | ||
168 | unsafe { | |
169 | let a = [0u8, 0u8, 0u8, 0u8]; | |
170 | ||
171 | let b = mem::transmute::<[u8; 4], u32>(a); | |
172 | } | |
173 | ``` | |
174 | ||
175 | We have to wrap the operation in an `unsafe` block for this to compile | |
176 | successfully. Technically, only the `mem::transmute` call itself needs to be in | |
177 | the block, but it's nice in this case to enclose everything related, so you | |
178 | know where to look. In this case, the details about `a` are also important, and | |
179 | so they're in the block. You'll see code in either style, sometimes the context | |
180 | is too far away, and wrapping all of the code in `unsafe` isn't a great idea. | |
181 | ||
182 | While `transmute` does very little checking, it will at least make sure that | |
183 | the types are the same size. This errors: | |
184 | ||
185 | ```rust,ignore | |
186 | use std::mem; | |
187 | ||
188 | unsafe { | |
189 | let a = [0u8, 0u8, 0u8, 0u8]; | |
190 | ||
191 | let b = mem::transmute::<[u8; 4], u64>(a); | |
192 | } | |
193 | ``` | |
194 | ||
195 | with: | |
196 | ||
197 | ```text | |
198 | error: transmute called with differently sized types: [u8; 4] (32 bits) to u64 | |
199 | (64 bits) | |
200 | ``` | |
201 | ||
202 | Other than that, you're on your own! |