Ключевое слово 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); }
Важные замечания:
asне выполняет проверки - это простое битовое преобразование- Может терять данные при числовых преобразованиях
- Небезопасно для некоторых преобразований указателей
- Альтернативы:
.try_into(),.into()для безопасных преобразований - Для сложных преобразований используйте
std::convert
Используйте as осторожно и только когда понимаете последствия преобразования!