类型转换
在编程语言中,类型转换分为 隐式类型转换 和 显式类型转换
Rust 是显式类型转换语言,只包含少部分的 隐式类型转换。
Deref 解引用
Rust 中的隐式类型转换,只有自动解引用。
自动解引用,是为了方便开发者使用 智能指针,比如: Box<T>,Rc<T>,String 等类型。
自动解引用
一般来说,引用使用 & 操作符,解引用使用 * 操作符。
自动解引用是编译器来做的,用户可以通过实现 Deref trait 来自定义 自动解引用的行为。
Deref 有一个特性是强制隐式转换,规则如下: 如果一个类型 T 实现了 Deref<Target=U>,则该类型 T 的引用(或智能指针) 在应用的时候会被自动转换为类型 U。
DerefMut 和 Deref 类似,只不过它是返回可变引用的,Deref 中包括关联类型 Target,它表示解引用之后的目标类型。
// Deref 和 MutDeref trait 的源码(简化)
pub trait Deref {
type Target: ?Sized;
fn deref(&self) -> &Self::Target;
}
pub trait DerefMut: Deref {
fn deref_mut(&mut self) -> &mut Self::Target;
}
// String 实现的自动解引用
impl ops::Deref for String {
type Target = str;
fn deref(&self) -> &str {
unsafe { str::from_utf8_unchecked(&self.vec) }
}
}
自动解引用样例:
fn main() {
let a: String = "hello ".to_string();
let b: String = "world".to_string();
// 此处将 &String 自动解引用为 &str ,因为 String 实现了 Deref<Target=&str>,见上一个代码片段。
let c: String = a + &b;
println!("c = {}", c); // c = hello world
}
除了 String 类型,标准库中常见的其他类型,如 Vec<T>、Box<T>、Rc<T>、Arc<T> 等均实现了 Deref。
实现 Deref 的目的,是为了简化编程,样例如下:
fn main() {
// Vec<T> 实现了 Deref
let v: Vec<i32> = vec![1, 2, 3];
foo(&v);
foo1(&v);
// Rc 指针实现了 Deref
let x: Rc<&str> = Rc::new("hello");
println!("x.chars() = {:?}", x.chars());
}
fn foo(v: &Vec<i32>) {
println!("v[0] = {:?}", v[0]);
}
fn foo1(v: &[i32]) {
println!("v[0] = {:?}", v[0]);
}
手动解引用
当 某类型 和其 解引用目标类型 中包含了相同的方法时,编译器无法判断要使用哪个,此时需要 手动解引用。
fn main() {
let x: Rc<&str> = Rc::new("hello");
let y: Rc<&str> = x.clone();
let z: &str = (*x).clone();
}
另外,match 引用时也需要手动解引用,如下所示:
fn main() {
let x = "hello".to_string();
match &(*x) {
"hello" => {
println!("hello")
}
_ => {
println!("none");
}
}
}
在上述情况下,需要手动解引用吧 String 类型转为 str,进而转为 &str 类型。
-
match x.deref(),直接调用 deref 方法,需要 use std::ops::Deref
-
match x.as_ref(),String 类型提供了 as_ref 方法来返回一个 &str 类型,改方法定义在 AsRef trait 中。
-
match x.borrow(),行为和 AsRef 类似,方法定义在 std::borrow::Borrow 中。
-
match &*x,使用 "解引用" 操作符 * ,将 String 转换为 str,然后在用 "引用"操作符 &,转为 &str 类型。
-
match &x[..], String 类型的 index 操作可返回 &str 类型。
as 操作符
基本类型转换
as 操作符最常用的场景就是转换 Rust 中的基本数据类型。as 操作符不支持重载。
原生类型使用 as 操作符进行类型转换的样例。
- 使用 as 操作符时,短类型 转换为 长类型 的时候是没问题的,反过来的时候,会做截断处理。
fn main() {
let a = 1u32;
let b = a as u64;
let x = 1u64;
let y = x as u32;
println!("a = {}, b = {}", a, b); // a = 1, b = 1
println!("x = {}, y = {}", x, y); // x = 1, y = 1
let m = std::u32::MAX;
let n = m as u16;
println!("m = {}, n = {}", m, n); // m = 4294967295, n = 65535
let e = -1i32;
let f = e as u32;
println!("e = {}, f = {}", e, f); // e = -1, f = 4294967295
}
无歧义完全限定语法
结构体 S 实现了 trait A 和 B 相同名称的方法,可通过 无歧义完全限定语法 指明调用哪个实现,两种方法:
-
第一种是 当做 trait 的静态函数来调用。形如 A::test(&s, 1); B::test(&s, 1);
-
第二种是 使用 as 操作符, <S as A>::test(&s, 3); <S as B>::test(&s, 3);
方式2更明确一些,指明了结构体信息。
#[derive(Debug)]
struct S(i32);
trait A {
fn test(&self, i: i32);
}
trait B {
fn test(&self, i: i32);
}
impl A for S {
fn test(&self, i: i32) {
println!("From A, S = {:?}, i = {}", &self, i);
}
}
impl B for S {
fn test(&self, i: i32) {
println!("From B, S = {:?}, i = {}", &self, i);
}
}
fn main() {
let s = S(6);
// s.test(3); // compile error
A::test(&s, 1);
B::test(&s, 1);
<S as A>::test(&s, 3);
<S as B>::test(&s, 3);
}
类型和子类型相互转换
as 转换还可用于 类型 和 子类型 之间的转换。
尽管 Rust 没有结构体继承的概念,它却有子类型机制。在 Rust 中,子类型是针对生命周期存在的。
生命周期是代码的作用域,所以我们可以根据它们相互包含的关系判断他们的继承关系。
比如 &'static str 类型 是 &'a str 类型的子类型, 'a 和 'static 都是声明周期标记,其中 'a 是泛型标记, 是 &str 的通用形式,'static 则是特指 静态生命周期的 &str 字符串。
fn main() {
let a: &'static str = "hello";
let b: &str = a as &str;
let c: &'static str = b as &'static str;
}
From 和 Into
From 和 Into 是定义于 std::convert 模块中的两个 trait。它们定义了 from 和 into 两个方法。
pub trait From<T>: Sized {
fn from(_: T) -> Self;
}
pub trait Into<T>: Sized {
fn into(self) -> T;
}
使用 Into trait 的样例:
#[derive(Debug)]
struct Person {
name: String,
}
impl Person {
fn new<T: Into<String>>(in_obj: T) -> Self {
Person {
name: in_obj.into(),
}
}
}
fn main() {
let a: String = "hello".to_string();
let other_string: String = String::from("hello");
println!("other_string = {}", other_string);
let person = Person::new("xiaoming");
println!("person = {:?}", person); // person = Person { name: "xiaoming" }
}
关于 Into 的一条默认规则: 如果类型 U 实现了 From<T>,则 T 类型实例调用 into 方法就可以转换为 类型 U。
fn main() {
let aa = "strstr";
let bb: String = aa.into();
}