昨天我们学习了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用于实现从引用到引用的转换。

它们的定义如下:

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

pub trait AsMut<T> 
where
    T: ?Sized, 
{
    fn as_mut(&mut self) -> &mut T;
}

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

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

// As lifts over &mut
#[stable(feature = "rust1", since = "1.0.0")]
impl<T: ?Sized, U: ?Sized> AsRef<U> for &mut T
where
    T: AsRef<U>,
{
    fn as_ref(&self) -> &U {
        <T as AsRef<U>>::as_ref(*self)
    }
}


// AsMut lifts over &mut
#[stable(feature = "rust1", since = "1.0.0")]
impl<T: ?Sized, U: ?Sized> AsMut<U> for &mut T
where
    T: AsMut<U>,
{
    fn as_mut(&mut self) -> &mut U {
        (*self).as_mut()
    }
}

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

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

下面看两个例子。

例1:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
fn print_ref(v: impl AsRef<[i32]>) {
    println!("{:?}", v.as_ref());
 }
 
 fn print(s: &[i32]) {
     println!("{:?}", s);
 }
 
 
 fn main() {
     let vec = vec![1, 2, 3];

     // 因为Vec<i32>实现了AsRef<[i32]>,则&Vec<i32>也实现了AsRef<[i32]>
     print_ref(&vec); // [1, 2, 3]

      // Vec<T>实现了AsRef<[T]>, as_ref可以完成从&Vec<T>到&[T]的转换
     print(&vec); // [1, 2, 3]
 

     // 即Vec<i32>实现了AsRef<[i32]>
     print_ref(vec); // [1, 2, 3]
     
 }

例2:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
fn print_ref(v: impl AsRef<str>) {
    println!("{}", v.as_ref());
}

  
 #[allow(dead_code)]
 enum Msg {
     Hello,
     World,
 }
 
 impl AsRef<str> for Msg {
     fn as_ref(&self) -> &str {
         match self {
             Msg::Hello => "hello",
             Msg::World => "world",
         }
     }
 }
 
 
 fn main() {
     // s1是&str类型, str类型实现了AsRef<str>,&str也实现了AsRef<str>
     let s1 = "hello";
     // s2是String类型, 实现了AsRef<str>, &String也实现了AsRef<str>
     let s2 = String::from("word");
     // msg是Msg类型, 实现了AsRef<str>, &Msg也实现了AsRef<str>
     let msg = Msg::Hello;
 
     // str类型实现了AsRef<str>
     print_ref(s1); // hello
     // &str类型实现了AsRef<str>
     print_ref(&s1); // hello
 
     // &String实现了AsRef<str>
     print_ref(&s2);// world
     // String实现了AsRef<str>
     print_ref(s2); // world
     
     //  &Msg实现了AsRef<str>
     print_ref(&msg); // hello
     // Msg实现了AsRef<str>
     print_ref(msg); // hello
 }

使用场景

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

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

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

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

参考