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

时间:2019-01-28 11:15:43

标签: rust traits coercion

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

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]强制转换为{em> ie 内联&[usize]而不使用letstub()

Playground

1 个答案:

答案 0 :(得分:1)

您要使用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);
}