1. 1. Introduction
  2. 2. Введение
  3. 3. C чего начать
  4. 4. Изучение Rust
    1. 4.1. Угадайка
    2. 4.2. Обедающие философы
    3. 4.3. Вызов кода на Rust из других языков
  5. 5. Синтаксис и семантика
    1. 5.1. Связывание имён
    2. 5.2. Функции
    3. 5.3. Простые типы
    4. 5.4. Комментарии
    5. 5.5. Конструкция `if`
    6. 5.6. Циклы
    7. 5.7. Владение
    8. 5.8. Ссылки и заимствование
    9. 5.9. Время жизни
    10. 5.10. Изменяемость
    11. 5.11. Структуры
    12. 5.12. Перечисления
    13. 5.13. Конструкция `match`
    14. 5.14. Шаблоны сопоставления `match`
    15. 5.15. Синтаксис методов
    16. 5.16. Вектора
    17. 5.17. Строки
    18. 5.18. Обобщённое программирование
    19. 5.19. Типажи
    20. 5.20. Типаж `Drop`
    21. 5.21. Конструкция `if let`
    22. 5.22. Типажи-объекты
    23. 5.23. Замыкания
    24. 5.24. Универсальный синтаксис вызова функций
    25. 5.25. Контейнеры и модули
    26. 5.26. `const` и `static`
    27. 5.27. Атрибуты
    28. 5.28. Псевдонимы типов
    29. 5.29. Приведение типов
    30. 5.30. Ассоциированные типы
    31. 5.31. Безразмерные типы
    32. 5.32. Перегрузка операций
    33. 5.33. Преобразования при разыменовании
    34. 5.34. Макросы
    35. 5.35. Сырые указатели
    36. 5.36. Небезопасный код
  6. 6. Эффективное использование Rust
    1. 6.1. Стек и куча
    2. 6.2. Тестирование
    3. 6.3. Условная компиляция
    4. 6.4. Документация
    5. 6.5. Итераторы
    6. 6.6. Многозадачность
    7. 6.7. Обработка ошибок
    8. 6.8. Выбор гарантий
    9. 6.9. Интерфейс внешних функций
    10. 6.10. Типажи `Borrow` и `AsRef`
    11. 6.11. Каналы сборок
    12. 6.12. Using Rust without the standard library
  7. 7. Нестабильные возможности Rust
    1. 7.1. Плагины к компилятору
    2. 7.2. Встроенный ассемблерный код
    3. 7.3. Без stdlib
    4. 7.4. Внутренние средства
    5. 7.5. Элементы языка
    6. 7.6. Продвинутое руководство по компоновке
    7. 7.7. Тесты производительности
    8. 7.8. Синтаксис упаковки и шаблоны `match`
    9. 7.9. Шаблоны `match` для срезов
    10. 7.10. Ассоциированные константы
    11. 7.11. Пользовательские менеджеры памяти
  8. 8. Глоссарий
  9. 9. Syntax Index
  10. 10. Библиография

Приведение типов

Rust, со своим акцентом на безопасность, обеспечивает два различных способа преобразования различных типов между собой. Первый — as, для безопасного приведения. Второй — transmute, в отличие от первого, позволяет произвольное приведение типов и является одной из самых опасных возможностей Rust!

as

Ключевое слово as выполняет обычное приведение типов:

let x: i32 = 5;

let y = x as i64;Run

Оно допускает только определенные виды приведения типов:

let a = [0u8, 0u8, 0u8, 0u8];

let b = a as u32; // four eights makes 32Run

Это приведет к ошибке:

error: non-scalar cast: `[u8; 4]` as `u32`
let b = a as u32; // four eights makes 32
        ^~~~~~~~

Это «нескалярное преобразование», потому что у нас здесь преобразуются множественные значения: четыре элемента массива. Такие виды преобразований очень опасны, потому что они делают предположения о том, как реализованы множественные нижележащие структуры. Поэтому нам нужно что-то более опасное.

transmute

Функция transmute предоставляется внутренними средствами компилятора, и то, что она делает, является очень простым, но в то же время очень опасным. Она сообщает Rust, чтобы он воспринимал значение одного типа, как будто это значение другого типа. Это делается независимо от системы проверки типов, и поэтому полностью на ваш страх и риск.

В предыдущем примере, мы знаем, что массив из четырех u8 отображается в массив u32 должным образом, и поэтому мы хотим выполнить приведение. Если вместо as использовать transmute, то Rust позволит это сделать:

use std::mem;

unsafe {
    let a = [0u8, 0u8, 0u8, 0u8];

    let b = mem::transmute::<[u8; 4], u32>(a);
}Run

Для того чтобы компиляция прошла успешно, мы должны обернуть эту операцию в unsafe блок. Технически, только вызов mem::transmute должен быть выполнен в небезопасном блоке, но в данном случае хорошо было бы поместить в этот блок все необходимое, связаное с этим вызовом, чтобы было удобнее искать. В данном примере связаной необходимой переменной является a, и поэтому она находится в блоке. Код может быть в любом стиле, иногда контекст расположен слишком далеко, и тогда упаковка всего кода в unsafe не будет такой уж хорошей идеей.

Хотя при использовании transmute и выполняется очень мало проверок, но как минимум будет проверяться, что типы имеют одинаковый размер. Нижеприведенный код завершится ошибкой:

use std::mem;

unsafe {
    let a = [0u8, 0u8, 0u8, 0u8];

    let b = mem::transmute::<[u8; 4], u64>(a);
}Run

со следующим описанием:

error: transmute called on types with different sizes: [u8; 4] (32 bits) to u64
(64 bits)

Все, кроме этой одной проверки, на ваш страх и риск!