使用 impl Trait 时如何获得 Deref 强制(采取 2) [英] How to get Deref coercion when using impl Trait (take 2)
问题描述
这是一个特征(针对问题进行了简化),我想为每个表现得像切片的类型实现它:
Here is a trait (simplified for the question) which I'd like to implement for every type that behaves like a slice:
trait SliceLike {
type Item;
/// Computes and returns (owned) the first item in a collection.
fn first_item(&self) -> Self::Item;
}
注意Item
类型是关联类型;我希望 SliceLike
的每种类型都具有唯一的元素类型.
Note that the Item
type is an associated type; I want each type that is SliceLike
to have a unique element type.
这是一个全面实施的尝试:
Here is an attempt at a blanket implementation:
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()
}
}
例如,这将编译并运行:
For example, this compiles and runs:
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()
它无法编译:
But if I inline stub()
it fails to compile:
let data: [usize; 2] = [3, 4];
assert_eq!(data.first_item(), 3); // Fails.
assert_eq!([3, 4].first_item(), 3); // Fails.
整体实现使用 Deref
特性,编译器本身使用该特性将其他类型转换为切片.它将捕获所有行为也像切片的第三方类型.
The blanket implementation uses the Deref
trait that the compiler itself uses to turn other types into slices. It will catch all third-party types that also behave like a slice.
错误信息是:
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
.
In take 1 of this question, I was advised to use AsRef
instead of Deref
. That solution won't work here, because some type might implement AsRef
for more than one element type.
我想我明白发生了什么.对于每种类型T
,都有一个唯一的类型
.当 T
为 &[usize;2]
目标是[usize;2]
,而不是 [usize]
.编译器能够强制 &[T;2]
到 &[T]
如果我明确要求它,例如通过使用 let
或 stub()
,但如果我不这样做,则无法确定需要强制转换.
I think I understand what is going on. For each type T
there is a unique type <T as Deref>::Target
. When T
is &[usize; 2]
the target is [usize; 2]
, not [usize]
. The compiler is able to coerce &[T; 2]
to &[T]
if I explicitly ask it to, e.g. by using let
or stub()
, but if I don't then it's not able to work out that the coercion is required.
但它令人沮丧:对于人类来说失败的调用打算做什么是非常明显的,并且编译器理解Vec
、Box<[usize]>的要求;
, Rc<[usize]>
, &[usize]
等等,所以尝试让它工作似乎不是不合理的对于 [usize;2]
也是.
But it's frustrating: it's perfectly obvious to a human what the failing calls are intended to do, and the compiler understands what's required for Vec<usize>
, Box<[usize]>
, Rc<[usize]>
, &[usize]
and so on, so it doesn't seem unreasonable to try to make it work for [usize; 2]
as well.
是否有一种方便的方法来编写 first()
以便最后两个调用也能工作?如果没有,是否有语法要求编译器强制一个 &[usize;2]
到 &[usize]
内联,ie 不使用 let
或 stub()
>?
Is there a convenient way to write first()
so that the last two calls work too? If not, is there a syntax to ask the compiler to coerce a &[usize; 2]
to a &[usize]
inline, i.e. without using let
or stub()
?
推荐答案
Deref
是为 Vec
, Box
, Rc
, &T where T: ?Sized
并且没有t 数组的实现 ([T; N]
),这就是 [3, 4].first_item()
不起作用的原因.
Deref
is implemented for Vec
, Box
, Rc
, &T where T: ?Sized
and there isn't an implementation for arrays ([T; N]
), that is why [3, 4].first_item()
doesn't work.
不可能为 [T; 实现
由于一致性规则 因此,必须以某种方式将数组强制转换为切片.我所知道的最好的方法如下:Deref
N]
It isn't possible to implement Deref
for [T; N]
due to coherence rules, therefore, the array must be coerced to a slice one way or another. The best method I am aware of is as follows:
let data: [usize; 2] = [3, 4];
assert_eq!((&data[..]).first_item(), 3); // Ok
请注意,一旦 const generic 合并.
Please note that issues like this are probably going to disappear once const generic is merged.
这篇关于使用 impl Trait 时如何获得 Deref 强制(采取 2)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!