我从头到尾阅读了 Rust 的确切自动取消引用规则是什么? 但我仍然对从数组到切片的强制有疑问。
让我们考虑以下代码:
let arr: &[i32; 5] = &&&[1, 2, 3, 4, 5];
// let arr: &[i32] = &&&[1, 2, 3, 4, 5]; // Error; expected slice, found reference
我希望&&&[1, 2, 3, 4, 5]
具有类型,&&&[i32; 5]
和取消引用&&[i32; 5]
=>&[i32; 5]
=>&[i32; 5]
=>&[i32]
, 但结果与我的预期不同。
我尝试运行以下代码:
let arr: &&&[i32; 5] = &&&[1, 2, 3, 4, 5];
let n = arr.first().unwrap(); // 1
这是正确的代码。arr
的类型被强制为&&&[i32; 5]
=>&&[i32; 5]
=>&[i32; 5]
=>&[i32]
并与切片中first
的第一个参数匹配,&self
。
数组强制切片的条件是什么?我不明白前者代码和后代码之间的区别。
我还检查了源代码中的文档,猜测上面的问题与下面引用的句子有关;
然而,我们有时会在此过程中进行其他调整和强制,特别是调整大小(例如,从 [T; n] 转换为 [T])。
这种强制是有效的,但不是实现的。
数组不实现Deref
,所以强制&[T; n] -> &[T]
不是deref强制,并且与强制工作方式不同。相反,它被称为"未调整大小的强制",因为它将大小的类型([T; n]
)变成未大小的类型([T]
)。
也就是说,语言参考(不是规范性的,可能已经过时,但请耐心等待)列出了可能的胁迫,包括以下内容(着重号是后加的):
T_1
T_3
,其中T_1
胁迫T_2
和T_2
胁迫T_3
(传递案例)请注意,尚不完全支持此功能。
- TyCtor(
&T
或&mut T
&U
T
是否实现了Deref<Target = U>
。T
) 到 TyCtor(
U
),其中 TyCtor(T
) 是其中之一
&T
&mut T
*const T
*mut T
Box<T>
以及可以通过不大范围的胁迫从
T
获得U
。
最后一颗子弹,不大威压,是允许&[T; n]
胁迫&[T]
的原因.值得注意的是,这仅描述了一层引用;它不包括&&[T; n]
->&[T]
的情况(我们还需要Deref
胁迫)。
回到你的非工作示例:
let arr: &[i32] = &&&[1, 2, 3, 4, 5];
预期的胁迫是&&&[i32; 5]
->&[i32]
。我们可以弄清楚这种胁迫应该如何运作:
&[i32; 5]
通过调整大小来强制&[i32]
;&&[i32; 5]
胁迫Deref
&[i32; 5]
;- 因此,
&&[i32; 5]
通过传递性胁迫&[i32]
。 &&&[i32; 5]
胁迫Deref
&&[i32; 5]
;- 因此,
&&&[i32; 5]
通过传递性强制&[i32]
。
但事实并非如此。上面的引用暗示了原因:在传递情况下,它说"请注意,这还没有完全支持"。据我所知,根据问题 #18602,"不完全支持"是一种对冲;说"未执行"会更准确。因此,就目前而言,通过传递性进行强制根本不可能。显然,这个问题不是一个高优先级的问题,可能是因为大小的数组不是很常见。(我怀疑当常量泛型登陆时,这可能会成为更常见的抱怨,因为这可能会使数组更有用。
那么arr.first()
为什么有效呢?好吧,用于查找使用.
(点)运算符调用的方法的"自动取消引用规则"与强制规则不同。Autoderef 类似于手动取消引用任意次数,直到您使用给定方法获得某些内容(可以强制转换为类型)。这意味着您不需要传递强制来通过 autoderef 查找方法调用。
延伸阅读
RFC #401 描述了大多数强制的预期语义,但从未完全实现。问题 #18602 跟踪传递强制的状态。
Rustonomicon也有一章是关于胁迫的,似乎同意参考书。