我有一个Option<String>
和一个Option<&str>
的函数。我认为Option::as_ref
会起作用,因为&String
通常会自动转换为&str
,但不会转到此处。我收到这个错误:
sandbox.rs:6:27: 6:37 error: mismatched types:
expected `core::option::Option<&str>`,
found `core::option::Option<&collections::string::String>`
(expected str,
found struct `collections::string::String`) [E0308]
虽然this answer描述了如何从一个转换为另一个,但我仍然想知道为什么&String
不是强迫的&#34;&#34; (如果这是正确的术语)通常是&str
。
答案 0 :(得分:14)
您的期望是公平的。如果编译器具有将&String
转换为&str
的魔力,它应该能够将Option<&String>
转换为Option<&str>
或(就此而言)AnyType<&String>
转换为{ {1}}。
但是在这种情况下,编译器几乎没什么魔力。矛盾的是,在(*)中尝试减少魔法时出现了强制性。要理解这一点,你必须了解强制和再生之间的联系,并跟随我在Rust历史中的一些小文章。
前段时间,在Rust中,您可以相对经常看到像AnyType<&str>
这样的构造:“再借”。对于像&*x
这样的指针类型,您希望能够使用Box
取消引用它们以获取内部值。如果您需要*
但&i32
为x
,则可以使用Box<i32>
重新借用,这实际上是两个操作的组合,取消引用{ {1}}使用&*x
并使用Box
对其内容进行新的引用。 *
需要很多编译器魔法来启用它。
所以我们的想法是:如果我们允许任何人决定&
取消引用对其类型的影响,我们会减少自定义指针所需的魔力,例如Box
,*
,{ {1}},允许在库中定义新的...然后Box
诞生了。
然而,Rust开发者更进了一步,以减少难看的再生。
如果您将Rc
传递给期望Arc
的内容,则可能需要执行此操作(顺便说一下still compiles):
Deref
所以(好的Rust开发人员去了)为什么我们不进行自动重新扩展,让人们只写&Box<i32>
?
如果某个类型
&i32
(例如fn main() { let a = &Box::new(2); test(&**a); // one * to dereference the Box, // one * to dereference the & // reborrow with & } fn test(a: &i32) { println!("{}", *a) }
)解析为test(a)
(T
), 我们会让Box<i32>
(U
)“强制”转到i32
(&T
)。
这可能会被认为是当前的强制规则。但是编译器执行的所有魔法都是通过根据需要使用&Box<i32>
s(调用&U
)来尝试重新借用。实际上,更多的是一个小客厅技巧。
现在,回到&i32
到*
强制。正如我们所看到的,编译器的魔力远不如我们所认为的那么神奇,并且解除引用 - 重新借用deref
不会导致预期的结果。即使您设法为AnyType<&String>
实施AnyType<&str>
并将&*AnyType<&String>
解析为Deref
,重新借用结果仍会产生AnyType
,而不是*AnyType<&String>
}。
因此,Rust的强制机制不能用于此。您需要明确告诉Rust如何从AnyType<str>
中取出&AnyType<str>
并重新放入AnyType<&str>
。
作为针对您的具体方案的一种解决方法,如果您的&String
功能只有一个并且在您的控制之下,您可以将其概括为AnyType
而不是&str
,就像这样:
Option<&str>
(*)Rust devs绝对是反魔法的,那些麻瓜!
答案 1 :(得分:4)
Rust中常用两种版本:auto-ref和auto-deref。
自动参考只能将T
转换为&T
或&mut T
,因此与此无关。
Auto-deref自动调用Deref::deref
。 Option<T>
并未实施Deref
,因此无法保留。但是,String
deref
到&str
,这就是为什么你可以看到强制。
我们知道Option
没有实现Deref
,因为Deref::deref
返回一个与&self
具有相同生命周期的指针,而指针的生命周期要求意味着因此,指针必须与&self
一样长,所以除少数例外情况外,指针必须是某个预先存在的对象,而不是deref
调用中创建的对象。
这并不意味着以上是唯一的原因,它没有实现Deref
,尽管它是足够的。
答案 2 :(得分:3)
Rust语言定义了一种方法,通过&T
将&U
隐式转换为T
,用于特定的U
和Deref
对特征。重要的是要理解在某些情况下,&U
是构造值。例如,String
和&String
都不包含&str
,因此deref()
方法需要手动创建&str
。
但是,该语言不定义了隐式将X<&T>
转换为X<&U>
的方法。为了实现这一点,编译器必须知道一种方法来初始化给定X<&U>
的{{1}}(并且对于X<&T>
的不同值,它将是不同的)。您不能简单地采用X
的二进制表示形式,并将其重新解释为X<&T>
。事实上,X<&U>
和Option<&String>
甚至没有相同的尺寸!即使大小匹配,指针通常也不一样。
为了开发这个问题的通用解决方案,Rust必须实现更高级的类型(从Rust 1.6开始不可用)。对于该特定问题,还可能存在 ad hoc 解决方案,但在实施HKT时,它将变得多余。