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

1. Приведение числовых типов

fn main() {
    // Целочисленные преобразования
    let x: i32 = 42;
    let y: u64 = x as u64;
    println!("i32 {} -> u64 {}", x, y);
    
    // С плавающей точкой
    let float_num: f64 = 3.14;
    let int_num: i32 = float_num as i32; // Дробная часть отбрасывается
    println!("f64 {} -> i32 {}", float_num, int_num);
    
    // Беззнаковые в знаковые
    let unsigned: u8 = 200;
    let signed: i8 = unsigned as i8; // Может привести к переполнению
    println!("u8 {} -> i8 {}", unsigned, signed);
    
    // Между целыми разного размера
    let large: i128 = 1000;
    let small: i8 = large as i8; // Обрезание битов
    println!("i128 {} -> i8 {}", large, small);
}

2. Преобразования с плавающей точкой

fn main() {
    // Float <-> Integer
    let pi: f32 = 3.14159;
    let pi_int = pi as i32;
    println!("f32 {} -> i32 {}", pi, pi_int); // 3
    
    // Обратное преобразование
    let count: i32 = 42;
    let count_float = count as f64;
    println!("i32 {} -> f64 {}", count, count_float);
    
    // Между float типами
    let float32: f32 = 2.5;
    let float64: f64 = float32 as f64;
    println!("f32 {} -> f64 {}", float32, float64);
}

3. Работа с указателями

fn main() {
    let x = 42;
    
    // Создание сырых указателей
    let raw_ptr: *const i32 = &x as *const i32;
    println!("Raw pointer: {:?}", raw_ptr);
    
    // Преобразование между типами указателей
    let mut y = 100;
    let const_ptr: *const i32 = &y as *const i32;
    let mut_ptr: *mut i32 = &mut y as *mut i32;
    
    // Преобразование указателей разных типов
    let byte_ptr = const_ptr as *const u8;
    println!("i32 pointer: {:?} -> u8 pointer: {:?}", const_ptr, byte_ptr);
    
    // Приведение к usize для арифметики указателей
    let addr = const_ptr as usize;
    println!("Pointer as usize: 0x{:x}", addr);
}

4. Преобразования с enum

fn main() {
    // Enum без данных -> целые числа
    enum Color {
        Red = 0xff0000,
        Green = 0x00ff00,
        Blue = 0x0000ff,
    }
    
    let color_value = Color::Red as i32;
    println!("Color Red as i32: 0x{:x}", color_value);
    
    // C-like enums
    #[repr(u8)]
    enum Status {
        Ok = 0,
        Error = 1,
        Loading = 2,
    }
    
    let status_code = Status::Error as u8;
    println!("Status code: {}", status_code);
    
    // Bool -> integer
    let true_as_int = true as i32; // 1
    let false_as_int = false as i32; // 0
    println!("true as i32: {}, false as i32: {}", true_as_int, false_as_int);
}

5. Работа с массивами и срезами

fn main() {
    // Приведение ссылок на массивы
    let arr: [i32; 4] = [1, 2, 3, 4];
    let slice: &[i32] = &arr as &[i32];
    println!("Array as slice: {:?}", slice);
    
    // Байтовые представления
    let value: u32 = 0x12345678;
    let bytes: &[u8] = unsafe {
        std::slice::from_raw_parts(
            &value as *const u32 as *const u8,
            std::mem::size_of::<u32>()
        )
    };
    println!("u32 0x{:x} as bytes: {:?}", value, bytes);
    
    // Приведение размеров срезов (осторожно!)
    let int_slice: &[i32] = &[1, 2, 3, 4];
    let byte_slice: &[u8] = unsafe {
        std::slice::from_raw_parts(
            int_slice.as_ptr() as *const u8,
            int_slice.len() * std::mem::size_of::<i32>()
        )
    };
    println!("i32 slice as u8 slice: {:?}", byte_slice);
}

6. Преобразования в трейт-объекты

trait Animal {
    fn speak(&self);
}

struct Dog;
struct Cat;

impl Animal for Dog {
    fn speak(&self) {
        println!("Woof!");
    }
}

impl Animal for Cat {
    fn speak(&self) {
        println!("Meow!");
    }
}

fn main() {
    let dog = Dog;
    let cat = Cat;
    
    // Создание трейт-объектов
    let animal1: &dyn Animal = &dog as &dyn Animal;
    let animal2: &dyn Animal = &cat as &dyn Animal;
    
    animal1.speak();
    animal2.speak();
    
    // Boxed trait objects
    let boxed_animal: Box<dyn Animal> = Box::new(Dog) as Box<dyn Animal>;
    boxed_animal.speak();
}

7. Явное приведение типов в выражениях

fn main() {
    // Для разрешения неоднозначности
    let result1 = 10 as f64 / 3.0;
    let result2 = 10 / 3 as f64;
    println!("10 as f64 / 3.0 = {}", result1); // 3.333...
    println!("10 / 3 as f64 = {}", result2);   // 3.0
    
    // В арифметических операциях
    let a: u8 = 100;
    let b: u8 = 200;
    let sum = a as u16 + b as u16; // Предотвращаем переполнение
    println!("{} + {} = {} (as u16)", a, b, sum);
    
    // При работе с индексами
    let arr = [10, 20, 30, 40];
    let index: i32 = 2;
    let element = arr[index as usize]; // Приведение для индексации
    println!("arr[{}] = {}", index, element);
}

8. Преобразования с char

fn main() {
    // Char <-> числовые типы
    let letter = 'A';
    let letter_code = letter as u8;
    println!("'{}' as u8: {}", letter, letter_code);
    
    let number = 66u8;
    let character = number as char;
    println!("{} as char: '{}'", number, character);
    
    // Unicode символы
    let heart = '💖';
    let heart_code = heart as u32;
    println!("'{}' as u32: 0x{:x}", heart, heart_code);
    
    // Осторожно: не все числовые значения - валидные char
    let invalid_char = 0xD800 as char; // Это surrogate pair, не валидный char!
    println!("Invalid char: {:?}", invalid_char); // Может привести к неопределенному поведению
}

9. Приведение в замыканиях и функциях

fn main() {
    // Приведение типов функций
    fn normal_function(x: i32) -> i32 {
        x * 2
    }
    
    let function_ptr: fn(i32) -> i32 = normal_function as fn(i32) -> i32;
    println!("Function result: {}", function_ptr(21));
    
    // Замыкания (требуют осторожности)
    let closure = |x: i32| x + 1;
    let closure_ptr: fn(i32) -> i32 = closure as fn(i32) -> i32;
    println!("Closure result: {}", closure_ptr(10));
    
    // Приведение к указателям на функции
    type MathFn = fn(i32, i32) -> i32;
    
    fn add(a: i32, b: i32) -> i32 { a + b }
    fn multiply(a: i32, b: i32) -> i32 { a * b }
    
    let operations: [MathFn; 2] = [
        add as MathFn,
        multiply as MathFn,
    ];
    
    for op in operations.iter() {
        println!("Operation result: {}", op(5, 3));
    }
}

10. Ограничения и безопасность

fn main() {
    // НЕЛЬЗЯ приводить между произвольными типами
    struct Point { x: i32, y: i32 }
    // let p = Point { x: 1, y: 2 };
    // let invalid = p as String; // Ошибка компиляции!
    
    // Осторожно с потерями точности
    let large: u64 = u64::MAX;
    let small: u32 = large as u32; // Потеря данных!
    println!("u64::MAX {} -> u32 {}", large, small);
    
    // Приведение с насыщением (альтернатива)
    let saturated = large.min(u32::MAX as u64) as u32;
    println!("Saturated conversion: {}", saturated);
}

Важные замечания:

  1. as не выполняет проверки - это простое битовое преобразование
  2. Может терять данные при числовых преобразованиях
  3. Небезопасно для некоторых преобразований указателей
  4. Альтернативы: .try_into(), .into() для безопасных преобразований
  5. Для сложных преобразований используйте std::convert

Используйте as осторожно и только когда понимаете последствия преобразования!