我从头到尾读了What are Rust's exact auto-dereferencing rules?,但我仍然有一个关于从数组到切片的强制的问题。
让我们考虑以下代码:
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
。
数组强制切片的条件是什么?我不明白前者和后者代码之间的区别。
我还检查了the documentation in the source code,并猜测上述问题与下面引用的句子有关;
然而,我们有时会沿途进行其他调整和强制,特别是不合规(例如,从[T; n]转换为[T])。
这种胁迫旨在起作用,但没有实施。
Arrays没有实施Deref
,所以胁迫&[T; n] -> &[T]
不是一个deref强制,并没有与一个完全相同的方式工作。相反,它被称为“未确定的强制”,因为它将一个大小的类型([T; n]
)变成一个未大小的类型([T]
)。
那就是说,language reference(不是规范性的,可能已经过时,但对我而言)列出了可能的强制措施,包括以下内容(强调增加):
T_1
到T_3
,T_1
强迫T_2
和T_2
胁迫T_3
(传递案) 请注意,尚未完全支持此功能- 如果
&T
实施&U
,T
到Deref<Target = U>
。- TyCtor(
T
)到TyCtor(U
),其中TyCtor(T
)是其中之一&T
&mut T
*const T
*mut T
Box<T>
并且T
可以从U
获得unsized coercion。
最后一个子弹,未经证实的强制,是允许&[T; n]
强迫&[T]
。值得注意的是,这仅描述了一层引用;它没有涵盖&&[T; n]
- > &[T]
案件(我们也需要Deref
强制)。
回到你的非工作范例:
let arr: &[i32] = &&&[1, 2, 3, 4, 5];
预期的强制是&&&[i32; 5]
- > &[i32]
。我们可以弄清楚这种强制应该如何运作:
&[i32; 5]
通过不合格来强迫&[i32]
;&&[i32; 5]
通过&[i32; 5]
强迫Deref
;&&[i32; 5]
通过传递性强迫&[i32]
。&&&[i32; 5]
通过&&[i32; 5]
强迫Deref
;&&&[i32; 5]
通过传递性强迫&[i32]
。但事实并非如此。上面的引用暗示了原因:在传递的情况下,它说“请注意,这还没有完全支持”。根据issue #18602的说法,据我所知,“不完全支持”是一种对冲;说“未实现”会更准确。所以,就目前而言,通过传递性的强制是不可能的。显然这个问题不是一个高优先级,可能是因为大小的数组并不常见。 (我怀疑当const泛型登陆时,这可能会成为一种更常见的抱怨,因为这可能会使数组更有用。)
那么为什么arr.first()
工作?好吧,用于查找用"auto-dereferencing rules"(点)运算符调用的方法的.
是强制规则的扩展。 Autoderef类似于手动解除引用任何次数,直到您使用给定方法获得某些内容(可以强制转换为某种类型)。这意味着您不需要传递性来通过autoderef(RFC 401称为“接收器强制”)来查找方法调用。
RFC #401描述了大多数强制的预期语义。这个RFC在5年前就被合并了。从那时起,许多事情都发生了变化,但它仍未完全实现(其跟踪问题是#18469),因此RFC 401无法准确描述Rust的任何过去,现在或未来版本。然而,RFC 401也允许将&&&[i32; 5]
强制到&[i32]
并且几乎采用相同的逻辑。
Rustonomicon也有关于强制的章节,似乎同意参考书。