Вот черта (упрощенная для вопроса), которую я хотел бы реализовать для каждого типа, который ведет себя как срез:
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()
?
Детская площадка