使用 impl Trait 时如何获得 Deref 强制

How to get Deref coercion when using impl Trait

此函数returns 类列表集合的第一个元素。它适用于各种不同的类似列表的类型:

fn first<T: Copy>(x: impl Deref<Target=[T]>) -> T {
    x[0]
}

例如编译运行:

let data: Vec<usize> = vec![3, 4];
assert_eq!(first(data), 3);

let data: &[usize] = &[3, 4];
assert_eq!(first(data), 3);

let data: Rc<[usize]> = Rc::new([3, 4]);
assert_eq!(first(data), 3);

这也编译并运行:

fn stub(x: &[usize]) -> usize {
    first(x)
}

let data: &[usize; 2] = &[3, 4];
assert_eq!(stub(data), 3);

assert_eq!(stub(&[3, 4]), 3);

但是编译失败:

let data: &[usize; 2] = &[3, 4];
assert_eq!(first(data), 3); // Fails.

assert_eq!(first(&[3, 4]), 3); // Fails.

错误信息是:

type mismatch resolving `<&[usize; 2] as std::ops::Deref>::Target == [_]`

我想我明白发生了什么。对于每种类型 T,都有一个独特的类型 <T as Deref>::Target。当 T&[usize; 2] 时,目标是 [usize; 2],而不是 [usize]。如果我明确要求,编译器能够将 &[T; 2] 强制转换为 &[T],例如通过使用 letstub(),但如果我不这样做,则无法确定需要强制转换。

但这令人沮丧。对于人类来说,失败调用的目的是显而易见的,并且编译器理解 Vec<usize>Box<[usize]>Rc<[usize]>&[usize] 等需要什么,所以尝试让它也适用于 [usize; 2] 似乎并不合理。

问题:有没有方便的写法first()使得最后两个调用也能正常工作?如果没有,是否有语法要求编译器将 &[usize; 2] 强制转换为 &[usize] 内联, 而不使用 letstub()?

Playground.

您想使用 AsRef,而不是 Deref:

use std::rc::Rc;

fn first<T: Copy>(x: impl AsRef<[T]>) -> T {
    x.as_ref()[0]
}

fn main() {
    let data: Vec<usize> = vec![3, 4];
    assert_eq!(first(data), 3);

    let data: &[usize] = &[3, 4];
    assert_eq!(first(data), 3);

    let data: Rc<[usize]> = Rc::new([3, 4]);
    assert_eq!(first(data), 3);

    let data: &[usize; 2] = &[3, 4];
    assert_eq!(first(data), 3);
}