数据类型
类型转换
原生类型
Rust 不提供原生类型之间的隐式类型转换(coercion),但可以使用 as 关键字进行显 式类型转换(casting)。
当把任何类型转换为无符号类型 T 时,会不断加上或减去 (std::T::MAX + 1),直到值位于新类型 T 的范围内。
// 不显示类型转换产生的溢出警告。 #![allow(overflowing_literals)] fn main() { let decimal = 65.4321_f32; // 错误!不提供隐式转换 // let integer: u8 = decimal; // 改正 ^ 注释掉这一行 // 可以显式转换 let integer = decimal as u8; let character = integer as char; // A的ASCII码为65 println!("Casting: {} -> {} -> {}", decimal, integer, character); // 当把任何类型转换为无符号类型 T 时,会不断加上或减去 (std::T::MAX + 1) // 直到值位于新类型 T 的范围内。 let single_char = 'A'; let unicode = '😇'; println!("single char is {}", single_char as u8); println!("single unicode is {}", unicode as u8); println!("128519 mod 256 {}", 128519 % 256); // 1000 已经在 u16 的范围内 println!("1000 as a u16 is: {}", 1000 as u16); // 1000 - 256 - 256 - 256 = 232 // 事实上的处理方式是:从最低有效位(LSB,least significant bits)开始保留 // 8 位,然后剩余位置,直到最高有效位(MSB,most significant bit)都被抛弃。 // 译注:MSB 就是二进制的最高位,LSB 就是二进制的最低位,按日常书写习惯就是 // 最左边一位和最右边一位。 println!("1000 as a u8 is : {}", 1000 as u8); // -1 + 256 = 255 println!(" -1 as a u8 is : {}", (-1i8) as u8); // 对正数,这就和取模一样。 println!("1000 mod 256 is : {}", 1000 % 256); // 当转换到有符号类型时,(位操作的)结果就和 “先转换到对应的无符号类型, // 如果 MSB 是 1,则该值为负” 是一样的。 // 当然如果数值已经在目标类型的范围内,就直接把它放进去。 println!(" 128 as a i16 is: {}", 128 as i16); // 128 转成 u8 还是 128,但转到 i8 相当于给 128 取八位的二进制补码,其值是: println!(" 128 as a i8 is : {}", 128 as i8); // 重复之前的例子 // 1000 as u8 -> 232 println!("1000 as a u8 is : {}", 1000 as u8); // 232 的二进制补码是 -24 println!(" 232 as a i8 is : {}", 232 as i8); }
非原生类型
Rust 使用 trait 解决类型之间的转换问题。最一般的转换会用到 From 和 into 两个 trait。不过,即便常见的情况也可能会用到特别的 trait,尤其是 从 String 转换到别的类型,以及把别的类型转换到 String 时。
From 和 Into
From trait允许一种类型定义 “怎么根据另一种类型生成自己”,因此它提供了一种类型转换的简单机制。在标准库中有无数From 的实现,规定原生类型及其他常见类型的转换功能。
Into trait 就是把From trait 倒过来而已。也就是说,如果你为你的类型实现了From,那么同时你也就免费获得了Into。
use std::convert::From; #[derive(Debug)] struct Number { value: i32, } impl From<i32> for Number { fn from(item: i32) -> Self { Number { value: item } } } // 针对不同类型实现trait impl From<char> for Number { fn from(item: char) -> Self { Number { value: item as i32 } } } fn main() { let int = 5; // 试试删除类型说明 let num: Number = int.into(); let num = Number::from(int); let num = Number::from('A'); println!("My number is {:?}", num); }
TryFrom 和 TryInto
类似于From和Into,TryFrom 和TryInto是 类型转换的通用 trait。不同于 From/Into 的是,TryFrom和TryInto trait 用于易出错的转换,也正因如此,其返回值是Result型。
use std::convert::TryFrom; use std::convert::TryInto; #[derive(Debug, PartialEq)] struct EvenNumber(i32); impl TryFrom<i32> for EvenNumber { type Error = (); fn try_from(value: i32) -> Result<Self, Self::Error> { if value % 2 == 0 { Ok(EvenNumber(value)) } else { Err(()) } } } fn main() { // TryFrom assert_eq!(EvenNumber::try_from(8), Ok(EvenNumber(8))); assert_eq!(EvenNumber::try_from(5), Err(())); // TryInto let result: Result<EvenNumber, ()> = 8i32.try_into(); assert_eq!(result, Ok(EvenNumber(8))); let result: Result<EvenNumber, ()> = 5i32.try_into(); assert_eq!(result, Err(())); }
ToString 和 FromStr
ToString
要把任何类型转换成 String,只需要实现那个类型的 ToString trait。然而不要直接这么做,您应该实现fmt::Display trait,它会自动提供 ToString,并且还可以用来打印类型,就像 print! 一节中讨论的那样。
use std::string::ToString; use std::fmt::{Display, Formatter, Result}; struct Circle { radius: i32 } impl ToString for Circle { fn to_string(&self) -> String { format!("Circle of radius {:?}", self.radius) } } // Display trait 和 ToString trait 实现其中一个即可 impl Display for Circle { fn fmt(&self, f: &mut Formatter) -> Result { write!(f, "{}", self.radius) } } fn main() { let circle = Circle { radius: 6 }; println!("{}", circle.to_string()); }
`FromStr'
我们经常需要把字符串转成数字。完成这项工作的标准手段是用 parse 函数。我们得
提供要转换到的类型,这可以通过不使用类型推断,或者用 “涡轮鱼” 语法(turbo
fish,<>)实现。
只要对目标类型实现了 FromStr trait,就可以用 parse 把字符串转换成目标类型。
标准库中已经给无数种类型实现了 FromStr。如果要转换到用户定义类型,只要手动实现
FromStr 就行。
fn main() { let parsed: i32 = "5".parse().unwrap(); let turbo_parsed = "10".parse::<i32>().unwrap(); let sum = parsed + turbo_parsed; println!{"Sum: {:?}", sum}; }
类型别名
可以用 type 语句给已有的类型取个新的名字。类型的名字必须遵循大驼峰命名。也可用注解忽略#[allow(non_camel_case_types)].