Skip to content

16.4 有时候需要手动处理

如果智能指针中的方法与它内部成员的方法冲突了怎么办呢?编译器会优先调用当前最匹配的类型,而不会执行自动deref,在这种情况下,我们就只能手动deref来表达我们的需求了。

比如说,Rc 类型和 String 类型都有clone方法,但是它们执行的任务不同。Rc::clone()做的是把引用计数指针复制一份,把引用计数加 1。String::clone()做的是把字符串深复制一份。示例如下:


rust
use std::rc::Rc;
use std::ops::Deref;
fn type_of(_: ()) { }

fn main() {
    let s = Rc::new(Rc::new(String::from("hello")));

    let s1 = s.clone();        // (1)
    //type_of(s1);
    let ps1 = (*s).clone();    // (2)
    //type_of(ps1);
    let pps1 = (**s).clone();  // (3)
    //type_of(pps1);
}

在以上的代码中,位置(1)处s1的类型为Rc<Rc<String>>,位置(2)处ps1的类型为Rc<String>,位置(3)处pps1的类型为 String。

一般情况下,在函数调用的时候,编译器会帮我们尝试自动解引用。但在某些情况下,编译器不会为我们自动插入自动解引用的代码。以 String 和&str类型为例,在 match 表达式中:


rust
fn main() {
    let s = String::new();
    match &s {
        "" => {}
        _ => {}
    }
}

这段代码编译会发生错误,错误信息为:


rust
mismatched types:
    expected `&collections::string::String`,
        found `&'static str`

match后面的变量类型是&String,匹配分支的变量类型为&'static str,这种情况下就需要我们手动完成类型转换了。手动将&String类型转换为&str类型的办法如下。

1)match s.deref()。这个方法通过主动调用deref()方法达到类型转换的目的。此时我们需要引入Deref trait方可通过编译,即加上代码use std::ops::Deref;

2)match &*s。我们可以通过*s运算符,也可以强制调用deref()方法,与上面的做法一样。

3)match s.as_ref()。这个方法调用的是标准库中的std::convert::AsRef方法,这个 trait 存在于 prelude 中,无须手工引入即可使用。

4)match s.borrow()。这个方法调用的是标准库中的std::borrow::Borrow方法。要使用它,需要加上代码use std::borrow::Borrow;

5)match &s[..]。这个方案也是可以的,这里利用了 String 重载的 Index 操作。

Released under the MIT License