使用 impl Trait 时如何获得 Deref 强制转换(第 2 课)
How to get Deref coercion when using impl Trait (take 2)
这是我想为每个行为类似于切片的类型实现的特征(针对问题进行了简化):
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`
在这个问题的中,有人建议我使用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()
?
Deref
是为 Vec
, Box
, Rc
, &T where T: ?Sized
实现的,没有为数组 ([T; N]
) 实现,这就是为什么 [3, 4].first_item()
不起作用。
由于 coherence rules,无法为 [T; N]
实现 Deref
,因此,必须以某种方式将数组强制为切片。我知道的最好的方法如下:
let data: [usize; 2] = [3, 4];
assert_eq!((&data[..]).first_item(), 3); // Ok
请注意,合并 const generic 后,此类问题可能会消失。
这是我想为每个行为类似于切片的类型实现的特征(针对问题进行了简化):
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`
在这个问题的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()
?
Deref
是为 Vec
, Box
, Rc
, &T where T: ?Sized
实现的,没有为数组 ([T; N]
) 实现,这就是为什么 [3, 4].first_item()
不起作用。
由于 coherence rules,无法为 [T; N]
实现 Deref
,因此,必须以某种方式将数组强制为切片。我知道的最好的方法如下:
let data: [usize; 2] = [3, 4];
assert_eq!((&data[..]).first_item(), 3); // Ok
请注意,合并 const generic 后,此类问题可能会消失。