|
1 | 1 | % Casting Between Types
|
2 | 2 |
|
3 |
| -Coming Soon |
| 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 | +# `as` |
| 9 | + |
| 10 | +The `as` keyword does basic casting: |
| 11 | + |
| 12 | +```rust |
| 13 | +let x: i32 = 5; |
| 14 | + |
| 15 | +let y = x as i64; |
| 16 | +``` |
| 17 | + |
| 18 | +It only allows certain kinds of casting, however: |
| 19 | + |
| 20 | +```rust,ignore |
| 21 | +let a = [0u8, 0u8, 0u8, 0u8]; |
| 22 | +
|
| 23 | +let b = a as u32; // four eights makes 32 |
| 24 | +``` |
| 25 | + |
| 26 | +This errors with: |
| 27 | + |
| 28 | +```text |
| 29 | +error: non-scalar cast: `[u8; 4]` as `u32` |
| 30 | +let b = a as u32; // four eights makes 32 |
| 31 | + ^~~~~~~~ |
| 32 | +``` |
| 33 | + |
| 34 | +It’s a ‘non-scalar cast’ because we have multiple values here: the four |
| 35 | +elements of the array. These kinds of casts are very dangerous, because they |
| 36 | +make assumptions about the way that multiple underlying strucutres are |
| 37 | +implemented. For this, we need something more dangerous. |
| 38 | + |
| 39 | +# `transmute` |
| 40 | + |
| 41 | +The `transmute` function is provided by a [compiler intrinsic][intrinsics], and |
| 42 | +what it does is very simple, but very scary. It tells Rust to treat a value of |
| 43 | +one type as though it were another type. It does this regardless of the |
| 44 | +typechecking system, and just completely trusts you. |
| 45 | + |
| 46 | +[intrinsic]: intrinsics.html |
| 47 | + |
| 48 | +In our previous example, we know that an array of four `u8`s represents a `u32` |
| 49 | +properly, and so we want to do the cast. Using `transmute` instead of `as`, |
| 50 | +Rust lets us: |
| 51 | + |
| 52 | +```rust |
| 53 | +use std::mem; |
| 54 | + |
| 55 | +unsafe { |
| 56 | + let a = [0u8, 0u8, 0u8, 0u8]; |
| 57 | + |
| 58 | + let b = mem::transmute::<[u8; 4], u32>(a); |
| 59 | +} |
| 60 | +``` |
| 61 | + |
| 62 | +We have to wrap the operation in an `unsafe` block, but this will compile |
| 63 | +successfully. Technically, only the `mem::transmute` call itself needs to be in |
| 64 | +the block, but it's nice in this case to enclose everything related, so you |
| 65 | +know where to look. In this case, the details about `a` are also important, and |
| 66 | +so they're in the block. You'll see code in either style, sometimes the context |
| 67 | +is too far away, and wrapping all of the code in `unsafe` isn't a great idea. |
| 68 | + |
| 69 | +While `transmute` does very little checking, it will at least make sure that |
| 70 | +the types are the same size. This errors: |
| 71 | + |
| 72 | +```rust,ignore |
| 73 | +use std::mem; |
| 74 | +
|
| 75 | +unsafe { |
| 76 | + let a = [0u8, 0u8, 0u8, 0u8]; |
| 77 | +
|
| 78 | + let b = mem::transmute::<[u8; 4], u64>(a); |
| 79 | +} |
| 80 | +``` |
| 81 | + |
| 82 | +with: |
| 83 | + |
| 84 | +```text |
| 85 | +error: transmute called on types with different sizes: [u8; 4] (32 bits) to u64 |
| 86 | +(64 bits) |
| 87 | +``` |
| 88 | + |
| 89 | +Other than that, you're on your own! |
0 commit comments