为什么对数组的嵌套引用不会强制切片? [英] Why doesn't a nested reference to an array coerce to a slice?
问题描述
我阅读了 Rust的确切自动引用规则是什么? 从头到尾,但我仍然对从数组到切片的强制转换有疑问。
让我们考虑以下代码:
let arr:& [i32; 5] =&& [1、2、3、4、5];
// //让arr:& [i32] =&& [1、2、3、4、5]; //错误;预期切片,找到参考
我希望&& [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强制,因此其工作方式与deref强制相同。取而代之的是,它被称为超大强制。因为它将大小类型( [T; n]
)转换为无大小类型( [T]
)。 / p>
也就是说,语言参考(不是规范性的,可能已经过时,但请允许我)列出了可能的强制,包括以下内容(添加了强调):
T_1
到T_3
,其中T_1
强制为T_2
和T_2
强制为T_3
( transitive case )
请注意,此功能尚未完全支持
& T
到& U
如果T
实现Deref< Target = U>
。
TyCtor(
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]
强制为& [i32; 5]
通过Deref
; - ,因此,
& [i32 ; 5]
通过传递性强制到& [i32]
。 -
& ;& [i32; 5]
强制为&& [i32; 5]
通过Deref
; - ,因此,
&& [i32; 5]
通过传递性强制到& [i32]
。
但是不是。上面的引用暗示了为什么:在传递情况下,它说注意,这还没有被完全支持。据我所知,根据 issue#18602 ,未完全支持的是套期保值;说未实现会更准确。因此,目前,通过传递性强制转换是完全不可能的。显然,此问题并不是一个高度优先的问题,可能是因为大小不大的阵列并不常见。 (我怀疑当const泛型登陆时,这可能会成为更常见的抱怨,因为这可能会使数组更有用。)
那为什么这样做 arr.first()
工作吗?好吧,自动引用规则 用于查找用调用的方法。
(点)运算符是强制规则的扩展。 Autoderef类似于手动取消引用多次,直到使用给定方法获得某些内容(可以强制转换为某种类型)为止。这意味着您不需要传递性即可通过autoderef(RFC 401称为接收方强制)找到方法调用。
进一步阅读
RFC#401 描述了大多数强制的预期语义。该RFC已于5年前合并。从那时起,许多事情发生了变化,但仍然没有完全实现(其跟踪问题是#18469 ),因此RFC 401无法准确地描述Rust的任何过去,现在或将来的版本。但是,RFC 401 也将允许强制&& [i32; 5]
到& [i32]
,并且逻辑几乎相同。
Rustonomicon 也有关于强制的一章,似乎与参考书一致。
I read What are Rust's exact auto-dereferencing rules? from beginning to end, but I still have a question about the coercion from array to slice.
Let us think about the following code:
let arr: &[i32; 5] = &&&[1, 2, 3, 4, 5];
// let arr: &[i32] = &&&[1, 2, 3, 4, 5]; // Error; expected slice, found reference
I would expect that &&&[1, 2, 3, 4, 5]
has the type, &&&[i32; 5]
and dereferences to &&[i32; 5]
=> &[i32; 5]
=> &[i32; 5]
=> &[i32]
,
but the result is different from what I expected.
I tried to run the following code:
let arr: &&&[i32; 5] = &&&[1, 2, 3, 4, 5];
let n = arr.first().unwrap(); // 1
That's the correct code. The type of arr
is coerced to &&&[i32; 5]
=> &&[i32; 5]
=> &[i32; 5]
=> &[i32]
and matches to the first argument of first
in slice, &self
.
What's the condition that arrays coerce to slices? I don't understand the difference between the former and the latter code.
I also checked the documentation in the source code, and guess that the above question has something to do with the sentence cited below;
However we sometimes do other adjustments and coercions along the way, in particular unsizing (e.g., converting from [T; n] to [T]).`
This kind of coercion is intended to work, but not implemented.
Arrays do not implement Deref
, so the coercion &[T; n] -> &[T]
is not a deref coercion and does not work in quite the same way as one. Instead, it's called an "unsized coercion" because it turns a sized type ([T; n]
) into an unsized one ([T]
).
That said, the language reference (which is not normative and may be outdated, but bear with me) lists the possible coercions, including the following (emphasis added):
T_1
toT_3
whereT_1
coerces toT_2
andT_2
coerces toT_3
(transitive case)Note that this is not fully supported yet
&T
to&U
ifT
implementsDeref<Target = U>
.TyCtor(
T
) to TyCtor(U
), where TyCtor(T
) is one of
&T
&mut T
*const T
*mut T
Box<T>
and where
T
can be obtained fromU
by unsized coercion.
The last bullet, unsized coercion, is what allows &[T; n]
to coerce to &[T]
. Notably, this only describes one layer of referencing; it doesn't cover the &&[T; n]
-> &[T]
case (for which we also need Deref
coercion).
Back to your non-working example:
let arr: &[i32] = &&&[1, 2, 3, 4, 5];
The intended coercion is &&&[i32; 5]
-> &[i32]
. We can work out how this coercion ought to work:
&[i32; 5]
coerces to&[i32]
by unsizing;&&[i32; 5]
coerces to&[i32; 5]
byDeref
;- therefore,
&&[i32; 5]
coerces to&[i32]
by transitivity. &&&[i32; 5]
coerces to&&[i32; 5]
byDeref
;- therefore,
&&&[i32; 5]
coerces to&[i32]
by transitivity.
But it doesn't. The quote above hints at why: under the transitive case, it says "Note that this is not fully supported yet". As far as I can tell, according to issue #18602, "not fully supported" is a hedge; it would be more accurate to say "unimplemented". So, for now, coercion via transitivity is not possible at all. Apparently this issue is not a high priority, probably because sized arrays aren't very common. (I suspect this might become a more common complaint when const generics land, since that may make arrays more useful.)
So why does arr.first()
work? Well, the "auto-dereferencing rules" used to find methods invoked with the .
(dot) operator are an extension of the coercion rules. Autoderef is similar to manually dereferencing any number of times until you get something (that can be coerced to a type) with the given method. This means you don't need transitivity to find method calls through autoderef (which RFC 401 calls "receiver coercion").
Further reading
RFC #401 describes intended semantics of most coercions. This RFC was merged over 5 years ago. Many things have changed since then, but it is still not fully implemented (its tracking issue is #18469), so RFC 401 does not accurately describe any past, present, or future version of Rust. Nevertheless, RFC 401 also would permit coercion of &&&[i32; 5]
to &[i32]
and by almost the same logic.
The Rustonomicon also has a chapter on coercions and appears to agree with the reference book.
这篇关于为什么对数组的嵌套引用不会强制切片?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!