"Expected trait A, found &A" 尝试装箱特征对象时
"Expected trait A, found &A" when trying to box a trait object
我正在尝试制作一个可以检索(和 return 引用)另一个特征的特征对象,或者创建一个(和 return 它的盒装版本),将选择留给实现者(这意味着我需要将 returned 对象的生命周期限制为生产者的生命周期)。但是,我 运行 出错了:
use std::borrow::Borrow;
use std::collections::HashMap;
trait A {
fn foobar(&self) {
println!("!");
}
}
trait ProducerOrContainer {
fn get_a<'a>(&'a self, name: &'a str) -> Option<Box<dyn A + 'a>>;
}
impl<'b, B: Borrow<A>> ProducerOrContainer for HashMap<&'b str, B> {
fn get_a<'a>(&'a self, name: &'a str) -> Option<Box<dyn A + 'a>> {
self.get(name).map(|borrow| Box::new(borrow.borrow()))
}
}
错误是:
error[E0308]: mismatched types
--> src/main.rs:20:9
|
20 | self.get(name).map(|borrow| Box::new(borrow.borrow()))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected trait A, found &A
|
= note: expected type `std::option::Option<std::boxed::Box<dyn A + 'a>>`
found type `std::option::Option<std::boxed::Box<&dyn A>>`
这让我很困惑,因为我希望 &A
也是 A
。我试过 impl<'a> A for &'a A
,但这也无济于事。有什么办法可以解决这个问题吗?
...that can either retrieve (and return a reference to) a trait object of another trait, or create one (and return a boxed version of it).
根据此要求,Box
将不起作用。 ABox
拥有它的数据,但有时你有借来的数据,你不能移动。
标准库中有一种叫做Cow
的类型,它是对一个值是被借用还是被拥有的抽象。但是,它在这里可能不太适合你,因为它不会让你拥有数据作为 Box
并且它还要求你的数据类型必须实现 ToOwned
.
但我们可以将您的要求直接建模为 enum
:
enum BoxOrBorrow<'a, T: 'a + ?Sized> {
Boxed(Box<T>),
Borrowed(&'a T),
}
并通过实施 Deref
:
使其符合人体工程学
use std::ops::Deref;
impl<'a, T> Deref for BoxOrBorrow<'a, T> {
type Target = T;
fn deref(&self) -> &T {
match self {
BoxOrBorrow::Boxed(b) => &b,
BoxOrBorrow::Borrowed(b) => &b,
}
}
}
这使您可以将自定义 BoxOrBorrow
类型视为任何其他引用 - 您可以使用 *
取消引用它或将其传递给任何需要引用 T
的函数。
这就是您的代码的样子:
trait ProducerOrContainer {
fn get_a<'a>(&'a self, name: &'a str) -> Option<BoxOrBorrow<'a, dyn A + 'a>>;
}
impl<'b, B: Borrow<dyn A>> ProducerOrContainer for HashMap<&'b str, B> {
fn get_a<'a>(&'a self, name: &'a str) -> Option<BoxOrBorrow<'a, dyn A + 'a>> {
self.get(name)
.map(|b| BoxOrBorrow::Borrowed(b.borrow()))
}
}
您可以通过为 &'_ dyn A
实现 A
并添加显式强制转换来编译原始代码:
self.get(name).map(|borrow| Box::new(borrow.borrow()) as Box<dyn A>)
闭包不是 coercion site。编译器查看闭包的内容以查看 return 值是什么,并得出它 returns Box<&'a dyn A>
的结论。但是闭包本身不能从 "function returning Box<&'a dyn A>
" 强制转换为 "function returning Box<dyn A + 'a>
",因为这些类型在结构上是不同的。您添加强制转换是为了告诉编译器您首先要将闭包 return Box<dyn A>
。
但这有点傻。 Box
引用在这里完全没有必要,将其转换为 Box<dyn A>
只是为调用者增加了另一个间接级别。 return 一种封装了“或者 盒装特征对象, 或 对特征的引用的想法的类型会更好对象”,如 所述。
在 Rust 的未来版本中,使用通用关联类型 ("GATs"),可以使 return 类型成为 ProducerOrContainer
的关联类型,类似于以下:
trait ProducerOrContainer {
type Result<'a>: A;
fn get_a<'a>(&'a self, name: &'a str) -> Option<Result<'a>>;
}
有了这个特征定义,每个实现 ProducerOrContainer
的类型都可以选择它 return 的类型,所以你可以为一些 impl
选择 Box<dyn A>
和 &'a dyn A
给其他人。但是,这在当前的 Rust (1.29) 中是不可能的。
我正在尝试制作一个可以检索(和 return 引用)另一个特征的特征对象,或者创建一个(和 return 它的盒装版本),将选择留给实现者(这意味着我需要将 returned 对象的生命周期限制为生产者的生命周期)。但是,我 运行 出错了:
use std::borrow::Borrow;
use std::collections::HashMap;
trait A {
fn foobar(&self) {
println!("!");
}
}
trait ProducerOrContainer {
fn get_a<'a>(&'a self, name: &'a str) -> Option<Box<dyn A + 'a>>;
}
impl<'b, B: Borrow<A>> ProducerOrContainer for HashMap<&'b str, B> {
fn get_a<'a>(&'a self, name: &'a str) -> Option<Box<dyn A + 'a>> {
self.get(name).map(|borrow| Box::new(borrow.borrow()))
}
}
错误是:
error[E0308]: mismatched types
--> src/main.rs:20:9
|
20 | self.get(name).map(|borrow| Box::new(borrow.borrow()))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected trait A, found &A
|
= note: expected type `std::option::Option<std::boxed::Box<dyn A + 'a>>`
found type `std::option::Option<std::boxed::Box<&dyn A>>`
这让我很困惑,因为我希望 &A
也是 A
。我试过 impl<'a> A for &'a A
,但这也无济于事。有什么办法可以解决这个问题吗?
...that can either retrieve (and return a reference to) a trait object of another trait, or create one (and return a boxed version of it).
根据此要求,Box
将不起作用。 ABox
拥有它的数据,但有时你有借来的数据,你不能移动。
标准库中有一种叫做Cow
的类型,它是对一个值是被借用还是被拥有的抽象。但是,它在这里可能不太适合你,因为它不会让你拥有数据作为 Box
并且它还要求你的数据类型必须实现 ToOwned
.
但我们可以将您的要求直接建模为 enum
:
enum BoxOrBorrow<'a, T: 'a + ?Sized> {
Boxed(Box<T>),
Borrowed(&'a T),
}
并通过实施 Deref
:
use std::ops::Deref;
impl<'a, T> Deref for BoxOrBorrow<'a, T> {
type Target = T;
fn deref(&self) -> &T {
match self {
BoxOrBorrow::Boxed(b) => &b,
BoxOrBorrow::Borrowed(b) => &b,
}
}
}
这使您可以将自定义 BoxOrBorrow
类型视为任何其他引用 - 您可以使用 *
取消引用它或将其传递给任何需要引用 T
的函数。
这就是您的代码的样子:
trait ProducerOrContainer {
fn get_a<'a>(&'a self, name: &'a str) -> Option<BoxOrBorrow<'a, dyn A + 'a>>;
}
impl<'b, B: Borrow<dyn A>> ProducerOrContainer for HashMap<&'b str, B> {
fn get_a<'a>(&'a self, name: &'a str) -> Option<BoxOrBorrow<'a, dyn A + 'a>> {
self.get(name)
.map(|b| BoxOrBorrow::Borrowed(b.borrow()))
}
}
您可以通过为 &'_ dyn A
实现 A
并添加显式强制转换来编译原始代码:
self.get(name).map(|borrow| Box::new(borrow.borrow()) as Box<dyn A>)
闭包不是 coercion site。编译器查看闭包的内容以查看 return 值是什么,并得出它 returns Box<&'a dyn A>
的结论。但是闭包本身不能从 "function returning Box<&'a dyn A>
" 强制转换为 "function returning Box<dyn A + 'a>
",因为这些类型在结构上是不同的。您添加强制转换是为了告诉编译器您首先要将闭包 return Box<dyn A>
。
但这有点傻。 Box
引用在这里完全没有必要,将其转换为 Box<dyn A>
只是为调用者增加了另一个间接级别。 return 一种封装了“或者 盒装特征对象, 或 对特征的引用的想法的类型会更好对象”,如
在 Rust 的未来版本中,使用通用关联类型 ("GATs"),可以使 return 类型成为 ProducerOrContainer
的关联类型,类似于以下:
trait ProducerOrContainer {
type Result<'a>: A;
fn get_a<'a>(&'a self, name: &'a str) -> Option<Result<'a>>;
}
有了这个特征定义,每个实现 ProducerOrContainer
的类型都可以选择它 return 的类型,所以你可以为一些 impl
选择 Box<dyn A>
和 &'a dyn A
给其他人。但是,这在当前的 Rust (1.29) 中是不可能的。