gpt4 book ai didi

generics - 确定类型时 match 和 unwrap_or 的区别

转载 作者:行者123 更新时间:2023-11-29 07:51:30 27 4
gpt4 key购买 nike

以下是我可以用 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 所以它可以变成一个 &str.

  2. 如果 unwrap_or 的参数是 &str,而不是将 b 键入为 &str,它发现 a 中保存的类型(即 &String)和 unwrap_or 的参数类型之间存在差异。

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

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

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

我在其他地方得到的一个让它们工作的答案是这样的:

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

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

最佳答案

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

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 , 这是 &str没有手动更改就无法成为( String::fromto_stringto_owned 等)

现在,我将尝试对原因做一个简短的解释,但请持保留态度,因为我不是该领域的专家 .


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

A String , 但是,是可以修改的。由于我们无法确定一个字符串中有多少个字符,它必须存在于“堆”中,如果某些东西试图“长成”其他东西,则对象可以在堆中移动,并且String type 基本上用作指向堆中该文本的指针,很像 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<&str>正在用 &String 展开作为默认值。这将编译,因为 &String => &str是一个有效的转换,编译器将自动插入必要的转换而不需要它是显式的。所以不,unwrap_or没有实现接受完全相同的对象类型;任何可以强制转换为 T 的对象(在这种情况下,T&str )是有效的。

换句话说,你的 unwrap_or示例失败,因为 一个 &str不能成为 &String ,而不是因为 unwrap_or方法异常挑剔。不,可能没有办法 unwrap_or不使用 String .你最好的选择是简单地使用 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| &s对于 map (引用在闭包末尾被丢弃)。

关于generics - 确定类型时 match 和 unwrap_or 的区别,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49396869/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com