昨天我们学习了Default trait,通过实现Default trait可以为类型提供缺省值。如果类型中的包含的其他类型都实现了Default trait,就可以通过derive宏#[derive(Default)]来为类型自动实现Default trait,当然也可以手工实现Default trait。

到目前为止我们已经学习了Rust标准库中的8个常用的trait:

  • std::str::FromStr
  • std::convert::From
  • std::convert::TryFrom
  • std::convert::Into
  • std::convert::TryInto
  • std::ops::Deref
  • std::ops::DerefMut
  • std::default::Default

其中From, TryFrom, Into, TryInto可以实现值类型到值类型之间的转换,今天将要学习的AsRef<T>和AsMut<T> trait可以实现引用类型到引用类型之间的转换。

AsRef<T>和AsMut<T>

std::convert::AsRef和std::convert::AsMut这两个trait用于实现从引用到引用的转换。

它们的定义如下:

 1pub trait AsRef<T> 
 2where
 3    T: ?Sized, 
 4{
 5    fn as_ref(&self) -> &T;
 6}
 7
 8pub trait AsMut<T> 
 9where
10    T: ?Sized, 
11{
12    fn as_mut(&mut self) -> &mut T;
13}

从定义可以看出以下规则:

  • 如果类型U实现了AsRef<T>,则as_ref可以实现&U&T的转换
  • 如果类型U实现了AsMut<T>,则as_ref可以实现从&U&mut T的转换

注意AsRef和AsMut的定义中都加了T: ?Sized的trait bound。

std::marker::Sized是Rust标准库中的一个标记trait,用于标记在编译时就能确定占用内存大小的类型。当使用泛型参数时,Rust编译器会自动为泛型参数加上T: Sized trait bound。例如struct Foo<T>等价于struct Foo<T: Sized>。 如果在某些情况下,无法在编译时确定泛型参数T类型的大小,就需要使用?Sized标记,即如果显式加了T: ?Sized的trait bound,那么T就可以是任意大小的。

AsRef和AsMut的定义中泛型参数T都加了T: ?Sized,所以它们都允许T使用大小可变的类型。

对于AsRef和AsMut,Rust还给了如下的泛型实现:

 1// As lifts over &
 2#[stable(feature = "rust1", since = "1.0.0")]
 3impl<T: ?Sized, U: ?Sized> AsRef<U> for &T
 4where
 5    T: AsRef<U>,
 6{
 7    fn as_ref(&self) -> &U {
 8        <T as AsRef<U>>::as_ref(*self)
 9    }
10}
11
12// As lifts over &mut
13#[stable(feature = "rust1", since = "1.0.0")]
14impl<T: ?Sized, U: ?Sized> AsRef<U> for &mut T
15where
16    T: AsRef<U>,
17{
18    fn as_ref(&self) -> &U {
19        <T as AsRef<U>>::as_ref(*self)
20    }
21}
22
23
24// AsMut lifts over &mut
25#[stable(feature = "rust1", since = "1.0.0")]
26impl<T: ?Sized, U: ?Sized> AsMut<U> for &mut T
27where
28    T: AsMut<U>,
29{
30    fn as_mut(&mut self) -> &mut U {
31        (*self).as_mut()
32    }
33}

对泛型实现可以用下面的规则描述:

  • 如果T实现了AsRef<U>,那么&T就实现了AsRef<U>
  • 如果T实现了AsRef<U>,那么&mut T就实现了AsRef<U>
  • 如果T实现了AsMut<U>,那么&mut T就实现了AsMut<U>

下面看两个例子。

例1:

 1fn print_ref(v: impl AsRef<[i32]>) {
 2    println!("{:?}", v.as_ref());
 3 }
 4 
 5 fn print(s: &[i32]) {
 6     println!("{:?}", s);
 7 }
 8 
 9 
10 fn main() {
11     let vec = vec![1, 2, 3];
12
13     // 因为Vec<i32>实现了AsRef<[i32]>,则&Vec<i32>也实现了AsRef<[i32]>
14     print_ref(&vec); // [1, 2, 3]
15
16      // Vec<T>实现了AsRef<[T]>, as_ref可以完成从&Vec<T>到&[T]的转换
17     print(&vec); // [1, 2, 3]
18 
19
20     // 即Vec<i32>实现了AsRef<[i32]>
21     print_ref(vec); // [1, 2, 3]
22     
23 }

例2:

 1fn print_ref(v: impl AsRef<str>) {
 2    println!("{}", v.as_ref());
 3}
 4
 5  
 6 #[allow(dead_code)]
 7 enum Msg {
 8     Hello,
 9     World,
10 }
11 
12 impl AsRef<str> for Msg {
13     fn as_ref(&self) -> &str {
14         match self {
15             Msg::Hello => "hello",
16             Msg::World => "world",
17         }
18     }
19 }
20 
21 
22 fn main() {
23     // s1是&str类型, str类型实现了AsRef<str>,&str也实现了AsRef<str>
24     let s1 = "hello";
25     // s2是String类型, 实现了AsRef<str>, &String也实现了AsRef<str>
26     let s2 = String::from("word");
27     // msg是Msg类型, 实现了AsRef<str>, &Msg也实现了AsRef<str>
28     let msg = Msg::Hello;
29 
30     // str类型实现了AsRef<str>
31     print_ref(s1); // hello
32     // &str类型实现了AsRef<str>
33     print_ref(&s1); // hello
34 
35     // &String实现了AsRef<str>
36     print_ref(&s2);// world
37     // String实现了AsRef<str>
38     print_ref(s2); // world
39     
40     //  &Msg实现了AsRef<str>
41     print_ref(&msg); // hello
42     // Msg实现了AsRef<str>
43     print_ref(msg); // hello
44 }

使用场景

AsRef一般用于轻量级的且不会失败的不同类型的引用之间的转换,如果所需转换的开销很大,最好用&T类型实现From trait或者写一个自定义函数。 AsMut除了用于可变引用之间的转换外,其它和AsRef类似。

我们在设计函数的时候,可以将AsRef作为函数参数,可以让我们在调用函数时传参更灵活。例如标准库的std::fs::File::open函数,它的签名是:

1pub fn open<P: AsRef<Path>>(path: P) -> Result<File>

open函数的参数path的类型要求是实现AsRef<Path>的类型,去查看文档的话,会发现str, String, Path等都实现了AsRef<Path>,所以在调用这个函数时我们可以直接传入&str, String和&Path类型。

参考