Как получить принуждение Deref при использовании Impl Trait (дубль 2) - PullRequest
0 голосов
/ 31 января 2019

Вот черта (упрощенная для вопроса), которую я хотел бы реализовать для каждого типа, который ведет себя как срез:

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`

В возьмите 1 этого вопроса, я былРекомендуется использовать AsRef вместо Deref.Это решение не будет работать здесь, потому что некоторый тип может реализовать AsRef для более чем одного типа элемента.

Я думаю, я понимаю, что происходит.Для каждого типа T существует уникальный тип <T as Deref>::Target.Когда T равно &[usize; 2], целью является [usize; 2], а не [usize].Компилятор может привести &[T; 2] к &[T], если я его прямо попросил, например, с помощью let или stub(), но если я этого не сделаю, он не сможет понять, что принуждение требуется.

Но это расстраивает: человеку совершенно очевидно, для чего предназначены ошибочные вызовы, и компилятор понимает, что требуется для Vec<usize>, Box<[usize]>, Rc<[usize]>, &[usize] и т. Д.так что не кажется бесполезным пытаться заставить его работать и для [usize; 2].

Есть ли удобный способ написать first(), чтобы последние два вызова тоже работали?Если нет, существует ли синтаксис, чтобы попросить компилятор принудительно установить &[usize; 2] в &[usize], , т.е. без использования let или stub()?

Детская площадка

1 Ответ

0 голосов
/ 07 февраля 2019

Deref реализовано для Vec, Box, Rc, &T where T: ?Sized и тамне является реализацией для массивов ([T; N]), поэтому [3, 4].first_item() не работает.

Невозможно реализовать Deref для [T; N] из-за когерентности правила , поэтому массив должен быть приведен к срезу так или иначе.Лучший из известных мне способов заключается в следующем:

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

Обратите внимание, что подобные проблемы, вероятно, исчезнут после объединения const generic .

...