为什么Option<& String>不强制选择<& str>?

时间:2016-01-24 10:37:57

标签: rust

我有一个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

3 个答案:

答案 0 :(得分:14)

您的期望是公平的。如果编译器具有将&String转换为&str的魔力,它应该能够将Option<&String>转换为Option<&str>或(就此而言)AnyType<&String>转换为{ {1}}。

但是在这种情况下,编译器几乎没什么魔力。矛盾的是,在(*)中尝试减少魔法时出现了强制性。要理解这一点,你必须了解强制和再生之间的联系,并跟随我在Rust历史中的一些小文章。

前段时间,在Rust中,您可以相对经常看到像AnyType<&str>这样的构造:“再借”。对于像&*x这样的指针类型,您希望能够使用Box取消引用它们以获取内部值。如果您需要*&i32x,则可以使用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::derefOption<T>并未实施Deref,因此无法保留。但是,String deref&str,这就是为什么你可以看到强制。

我们知道Option没有实现Deref,因为Deref::deref返回一个与&self具有相同生命周期的指针,而指针的生命周期要求意味着因此,指针必须与&self一样长,所以除少数例外情况外,指针必须是某个预先存在的对象,而不是deref调用中创建的对象。

这并不意味着以上是唯一的原因,它没有实现Deref,尽管它是足够的。

答案 2 :(得分:3)

Rust语言定义了一种方法,通过&T&U隐式转换为T,用于特定的UDeref对特征。重要的是要理解在某些情况下,&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时,它将变得多余。