【问题标题】:Difference between match and unwrap_or when determining types确定类型时 match 和 unwrap_or 之间的区别
【发布时间】:2018-08-30 00:20:40
【问题描述】:

以下是我可以用 Rust 1.23.0 编译的有效文件:

fn main() {
    let r = String::from("a");
    let a = Some(&r);
    let b = match a {
        Some(name) => name,
        None => "",
    };
    println!("{}", b);
}

下面的代码

fn main() {
    let r = String::from("a");
    let a = Some(&r);
    let b = a.unwrap_or("");
    println!("{}", b);
}

因错误而失败:

error[E0308]: mismatched types
 --> src/main.rs:4:25
  |
4 |     let b = a.unwrap_or("");
  |                         ^^ expected struct `std::string::String`, found str
  |
  = note: expected type `&std::string::String`
             found type `&'static str`

据我所知,这里确定类型时编译器的推理如下:

  1. match 的情况下,它确定b&str,因为None => ""&strSome(name) => name&String,所以它可以变成@987654331 @.

  2. 如果unwrap_or 的参数是&str,而不是将b 键入为&str,它会发现a 中保存的类型(即@ 987654337@) 和参数的类型为unwrap_or

这两种情况有什么区别使类型推导以这种方式工作?

unwrap_or 是否实现了接受与选项包装的类型完全相同的类型,而不是只接受它放在匹配项中的通用值?

此外,有什么方法可以让unwrap_or 在这种情况下工作,而无需在外部范围内声明String 或更改选项包装的类型?

我在其他地方得到的一个让它们起作用的答案是:

let b = a.map(|s| s.as_ref()).unwrap_or("")

match 的效果相同,略短,比match 更明确地说明类型发生了什么。

【问题讨论】:

    标签: generics rust option rust-result


    【解决方案1】:

    粗略地说,你说对了一半。

    a 是一个Option<&String>。可以将&String 强制转换为&str,但不能将&str 强制转换为&String。请参阅此代码:

    fn main() {
        let a = String::from("This is a String");
        let a_r = &a;
        foo(a_r);
        bar(a_r);
    
        let b = "This is an &str";
        // Next line will cause a compilation error if uncommented
        // foo(b);
        bar(b);
    }
    
    fn foo(s: &String) {
        println!("Foo: {}", s);
    }
    
    fn bar(s: &str) {
        println!("Bar: {}", s);
    }
    

    Playground link

    如果foo(b); 未注释,它会告诉你它期望String,得到str

    match 语句返回 &str(从 &String 转换而来),但 unwrap_or 需要与您正在调用的 Option 相同的类型,即 &String,即 @987654340 @ 不能在没有手动更改的情况下变为(String::fromto_stringto_owned 等)

    现在,我将尝试对为什么给出一个简短的解释,但要持保留态度,因为我不是该领域的专家强>.


    当使用字符串文字 ("foo") 时,程序在编译时会创建一些内存段,以二进制形式表示该文本。这就是为什么字符串文字是&str 而不仅仅是str:它不是原始文本,而是对启动时已经存在的文本的引用。这意味着字符串文字存在于无法修改的特定内存部分,因为它们被挤在其他一些对象旁边 - ("fooquxegg") - 并试图将字符添加到字符串文字会导致您覆盖行中的下一个对象,这是非常糟糕的。

    但是,String 是可修改的。由于我们无法确定一个字符串中有多少个字符,它必须存在于“堆”中,如果某物试图“长成”其他东西,对象可以在其中移动,并且String 类型基本上用作指向堆中该文本的指针,就像 Box<T> 指向堆中的 T

    出于这个原因,虽然&String 可以变成&str(它指向一定数量的文本某处,在这种情况下,在堆中的某处而不是静态内存中) , &str 不能保证成为 &String。如果您将一个字符串文字(例如我们的"foo")(它位于无法修改的内存部分)转换为一个可能被修改&String,然后尝试添加一些字符(比如"bar"),它会覆盖其他内容。


    话虽如此,看看这段代码:

    fn main() {
       let s = "this is an &str";
       let b = String::from("this is a String");
       let s_o : Option<&str> = Some(s);
    
       let s_or_def = s_o.unwrap_or(&b);
       println!("{}", s_or_def);
    }
    

    Playground link

    此代码实际上与您的 unwrap_or 示例相反:Option&lt;&amp;str&gt; 默认使用 &amp;String 展开。这将编译,因为&amp;String =&gt; &amp;str 是一个有效的转换,编译器将自动插入必要的转换,而不需要它是显式的。所以不,unwrap_or 没有实现接受完全相同的对象类型;任何可以强制转换为T 的对象(在这种情况下,T&amp;str)都是有效的。

    换句话说,您的unwrap_or 示例失败是因为&amp;str 不能成为&amp;String,而不是因为unwrap_or 方法异常挑剔。不,如果不使用String,可能无法使用unwrap_or。您最好的选择是在字符串文字上简单地使用 to_string,如下所示:

    fn main() {
        let s = String::from("Hello, world!");
        let o = Some(s);
        let r = o.unwrap_or("Goodbye, world!".to_string());
    
        println!("{}", r);
    }
    

    因为map_or 虽然看起来很有希望,但不会简单地允许您输入|s| &amp;s 来获取地图(引用在结束时被删除)。

    【讨论】:

    • 我的意思是,解释是正确的,但我更想知道“为什么”这是 unwrap_or 和 match 之间的实现差异(例如,为什么我们不能让 unwrap_or 表现得像 match)?另外,如果我想返回String,但如果我想要&amp;String(留职时间问题),你的例子就可以工作,这就是我想要的 .get in a map
    • 另外,我编辑了我的帖子来解释如何使用 .map。显然 .map(|s| s.as_ref()) 是你需要的
    • 我想我已尽我所能解释实现的不同之处:在match 的情况下,您将&amp;String 转换为&amp;str,这是可以接受的,而在unwrap_or 的情况下,您正在尝试将&amp;str 转换为&amp;String,但事实并非如此。 unwrap_or 需要 &amp;String 用于类型检查。
    • 如果您需要&amp;String,那么简单的答案是否定的,您需要在与unwrap_or 相同的范围内使用let default_string = "".to_string(),以便您可以创建对它的引用。至少,据我所知。
    • 再次说明一下。在您的match 情况下,b&amp;str,但在您的unwrap_or 情况下,b&amp;String。 Rust 定义函数的方式,以及哪些类型可以强制转换为其他类型。这就是它们不同的原因。
    猜你喜欢
    • 1970-01-01
    • 2012-12-09
    • 2017-07-22
    • 1970-01-01
    • 2014-09-10
    • 1970-01-01
    • 2013-10-05
    • 2016-09-28
    • 2013-11-17
    相关资源
    最近更新 更多