使用impl Trait时如何获得Deref强制(习题2)

时间:2019-01-30 23:50:36

标签: rust traits coercion

这是一个特征(为问题简化了),我想为每个像切片的类型实现该特征:

trait SliceLike {
    type Item;

    /// Computes and returns (owned) the first item in a collection.
    fn first_item(&self) -> Self::Item;
}

请注意,Item类型是关联类型;我希望每个SliceLike类型都具有唯一的元素类型。

这是尝试全面实施的方法:

use std::ops::Deref;

impl<T: Clone, U: Deref<Target = [T]>> SliceLike for U {
    type Item = T;

    fn first_item(&self) -> Self::Item {
        self[0].clone()
    }
}

例如,它将编译并运行:

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

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

let data: Box<[usize]> = Box::new([3, 4]);
assert_eq!(data.first_item(), 3);

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

这也可以编译并运行:

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

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

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

但是如果我内联stub(),它将无法编译:

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

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

通用实现使用Deref特性,编译器本身使用该特性将其他类型转换为片。它将捕获所有行为也像切片的第三方类型。

错误消息是:

error[E0599]: no method named `first_item` found for type `[usize; 2]` in the current scope
  --> src/lib.rs:20:21
   |
20 |     assert_eq!(data.first_item(), 3); // Fails.
   |                     ^^^^^^^^^^
   |
   = note: the method `first_item` exists but the following trait bounds were not satisfied:
           `[usize; 2] : SliceLike`
           `[usize] : SliceLike`
   = help: items from traits can only be used if the trait is implemented and in scope
   = note: the following trait defines an item `first_item`, perhaps you need to implement it:
           candidate #1: `SliceLike`

在此问题的take 1中,建议我使用AsRef而不是Deref。该解决方案在这里不起作用,因为某些类型可能会为多个元素类型实现AsRef

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

但是令人沮丧的是:对于人类来说,失败调用的意图是显而易见的,并且编译器理解Vec<usize>Box<[usize]>Rc<[usize]>,{{1 }}等,因此尝试使其也适用于&[usize]似乎并非没有道理。

是否有一种方便的方式来编写[usize; 2],以便最后两个调用也可以工作?如果不是,是否有语法要求编译器将first()强制转换为{em> ie 内联&[usize; 2]而不使用&[usize]let

Playground

1 个答案:

答案 0 :(得分:2)

Deref已针对VecBoxRc&T where T: ?Sized实现,并且没有针对数组([T; N])的实现,这就是[3, 4].first_item()不起作用的原因。

由于coherence rules,无法为Deref实现[T; N],因此,必须以一种或另一种方式将数组强制为片。我知道的最好的方法如下:

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

请注意,const generic合并后,类似的问题可能会消失。