为什么对数组的嵌套引用不会强制转换为切片?

Why doesn't a nested reference to an array coerce to a slice?

我从头到尾读了,但我对从数组到切片的强制转换仍有疑问。

让我们思考下面的代码:

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] => [=17 的取消引用=], 但是结果和我预想的不一样

我试过运行下面的代码:

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,猜测上面的问题和下面引用的句子有关系;

However we sometimes do other adjustments and coercions along the way, in particular unsizing (e.g., converting from [T; n] to [T]).`

这种强制是为了工作,但没有实施。

Arrays 没有实现 Deref,所以强制转换 &[T; n] -> &[T] 不是一个 deref 强制转换,并且工作方式与一个完全不同。相反,它被称为“未调整大小的强制转换”,因为它将调整大小的类型 ([T; n]) 转换为未调整大小的类型 ([T])。

也就是说,language reference(这不是规范的,可能已经过时了,但请耐心等待)列出了可能的强制转换,包括以下内容(强调已添加):

  • T_1 to T_3 where T_1 coerces to T_2 and T_2 coerces to T_3 (transitive case)

    Note that this is not fully supported yet.

  • &T or &mut T to &U if T implements Deref<Target = U>.

  • TyCtor(T) to TyCtor(U), where TyCtor(T) is one of

    • &T
    • &mut T
    • *const T
    • *mut T
    • Box<T>

    and where U can be obtained from T by unsized coercion.

最后一个项目符号,unsized 强制转换,允许 &[T; n] 强制转换为 &[T]。值得注意的是,这只描述了一层引用;它不包括 &&[T; n] -> &[T] 案例(为此我们还需要 Deref 强制)。

回到你的非工作示例:

let arr: &[i32] = &&&[1, 2, 3, 4, 5];

预期的强制转换是 &&&[i32; 5] -> &[i32]。我们可以弄清楚这种强制应该如何工作:

  1. &[i32; 5] 通过缩小大小强制转换为 &[i32]
  2. &&[i32; 5] 通过 Deref;
  3. 强制转换为 &[i32; 5]
  4. 因此,&&[i32; 5] 通过传递性强制转换为 &[i32]
  5. &&&[i32; 5] 通过 Deref;
  6. 强制转换为 &&[i32; 5]
  7. 因此,&&&[i32; 5] 通过传递性强制转换为 &[i32]

但事实并非如此。上面的引用暗示了原因:在传递情况下,它说“请注意,这还没有得到完全支持”。据我所知,根据 issue #18602,“未完全支持”是一种对冲;说“未实现”会更准确。因此,就目前而言,通过传递性进行强制转换根本不可能。显然这个问题不是一个高优先级,可能是因为大小数组不是很常见。 (我怀疑当 const 泛型出现时,这可能会成为更常见的抱怨,因为这可能会使数组更有用。)

那么为什么 arr.first() 有效?好吧,用于查找使用 .(点)运算符调用的方法的 与强制转换规则不同。 Autoderef 类似于手动取消引用任意次数,直到您使用给定的方法得到某些东西(可以强制转换为一种类型)。这意味着您不需要传递强制来通过 autoderef 查找方法调用。


进一步阅读

RFC #401 describes intended semantics of most coercions, but has never been fully implemented. Issue #18602 跟踪传递强制的状态。

Rustonomicon 也有一章是关于强制转换的,似乎与参考书一致。