以下是我可以使用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`
据我所知,编制者在确定类型时的推理如下:
如果match
确定b
为&str
,则None => ""
为&str
和Some(name) => name
是&String
,因此它可以成为&str
。
如果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
更明确,更明确地说明类型发生了什么。
答案 0 :(得分:2)
松散地说,你是正确的一半。
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);
}
如果取消注释foo(b);
,则会告诉您预期String
,str
。
match
语句返回&str
(从&String
转换而来),但unwrap_or
期望与您Option
的{{1}}类型相同。重新调用,&String
,&str
无法手动更改String::from
,to_string
,to_owned
等
现在,我将试着给为什么做一个简短的解释,但是把它当作一粒盐,因为我不是这个领域的专家强>
当使用字符串文字("foo"
)时,程序在编译时会创建一些内存段,以二进制形式表示此文本。这就是为什么字符串文字是&str
而不仅仅是str
:它不是原始文本,而是“ 对已经存在的文本的引用启动。这意味着字符串文字存在于无法修改的内存的特定部分中,因为它们与其他对象("fooquxegg"
)并列 - 并尝试添加字符字符串文字将导致你覆盖行中的下一个对象,这是非常糟糕的。
然而,String
是可修改的。由于我们无法确定字符串中有多少个字符,因此它必须存在于&#34;堆&#34;中,如果某些事物尝试&#,可以移动对象 34;成长为&#34;其他东西,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);
}
此代码实际上与您的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
(在关闭结束时删除引用)。