我在外部包装箱中定义了一个特征,我需要从我定义的结构中的方法中将其返回。接受trait类型作为输入参数没有问题,但是我不知道如何返回它。该特征未实现Sized
,并且我无法更改其实现。
这是示例代码(playground):
use std::fmt::Debug;
// this code is defined in an external crate
pub trait SomeTrait: Clone + Debug {
fn get_name(&self) -> &str;
}
#[derive(Clone, Debug)]
struct Implementor1(String);
impl SomeTrait for Implementor1 {
fn get_name(&self) -> &str {
&self.0
}
}
#[derive(Clone, Debug)]
struct Implementor2 {
name: String,
}
impl SomeTrait for Implementor2 {
fn get_name(&self) -> &str {
&self.name
}
}
// the code below is mine
struct ImplementorManager<T: SomeTrait> {
implementors: Vec<T>,
}
impl<T: SomeTrait> ImplementorManager<T> {
pub fn call_get_name(implementor: T) -> String {
implementor.get_name().to_string()
}
pub fn new_implementor(first: bool, name: &str) -> T {
match first {
true => Implementor1(name.to_string()),
false => Implementor2 {
name: name.to_string(),
},
}
}
}
fn main() {
let implementor = Implementor1("first".to_string());
println!("name: {}", ImplementorManager::call_get_name(implementor));
}
我得到的错误:
error[E0308]: mismatched types
--> src/main.rs:40:21
|
33 | impl<T: SomeTrait> ImplementorManager<T> {
| - this type parameter
...
38 | pub fn new_implementor(first: bool, name: &str) -> T {
| - expected `T` because of return type
39 | match first {
40 | true => Implementor1(name.to_string()),
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected type parameter `T`, found struct `Implementor1`
|
= note: expected type parameter `T`
found struct `Implementor1`
如果我要注释掉new_implementor()
方法,则call_get_name()
方法可以很好地接受特征。我已经尝试Box
处理返回的对象,但是如果没有Sized
特性,则不可能。
有什么办法可以克服吗?
我有点弄乱了我的解释和例子。让我再说一遍。
我想在结构中使用Peripheral
条板箱中的btleplug
结构。在Linux上,此结构是公共的,但在私有模块中。 api模块中仅公开Peripheral
特性。
这是示例代码:
use btleplug::api::{BDAddr, Central, Peripheral};
use btleplug::bluez::manager::Manager;
use btleplug::Error;
use std::str::FromStr;
// cannot import the Peripheral struct as the module is private
// use btleplug::bluez::adapter::peripheral::Peripheral;
struct MyStruct<PeripheralType: Peripheral> {
device: PeripheralType,
}
impl<PeripheralType> MyStruct<PeripheralType>
where
PeripheralType: Peripheral,
{
fn get_device() -> PeripheralType {
let central = Manager::new()
.unwrap()
.adapters()
.unwrap()
.into_iter()
.next()
.unwrap()
.connect()
.unwrap();
central
.peripheral(BDAddr::from_str("2A:00:AA:BB:CC:DD").unwrap())
.unwrap()
}
pub fn new() -> Self {
let device = Self::get_device();
Self { device }
}
}
fn main() -> Result<(), Error> {
let _ = MyStruct::new();
Ok(())
}
我得到的错误:
error[E0308]: mismatched types
--> src/main.rs:27:9
|
13 | impl<PeripheralType> MyStruct<PeripheralType>
| -------------- this type parameter
...
17 | fn get_device() -> PeripheralType {
| -------------- expected `PeripheralType` because of return type
...
27 | / central
28 | | .peripheral(BDAddr::from_str("2A:00:AA:BB:CC:DD").unwrap())
29 | | .unwrap()
| |_____________________^ expected type parameter `PeripheralType`, found struct `btleplug::bluez::adapter::peripheral::Peripheral`
|
= note: expected type parameter `PeripheralType`
found struct `btleplug::bluez::adapter::peripheral::Peripheral`
在某种程度上work internally似乎如此,但是我不明白为什么在我的示例中它不起作用...
答案 0 :(得分:1)
在此代码中:
impl<PeripheralType> MyStruct<PeripheralType>
where
PeripheralType: Peripheral,
{
fn get_device() -> PeripheralType {
...
central
.peripheral(BDAddr::from_str("2A:00:AA:BB:CC:DD").unwrap())
.unwrap()
}
您正在倒退类型依赖关系:您假设PeripheralType
为任意类型(这就是impl<PeripheralType>
的意思),然后尝试为其使用特定但无法命名的类型的值。< / p>
(附带说明:在Rust中使用 closures 时也会出现无法命名的类型-每个闭包定义都有一个唯一的无法命名的类型-因此这不是常见的问题。)
相反,要做此工作所需要做的是首先 获取值,然后然后为其构造结构。首先,这里是get_device
的定义应该起作用,因为impl Peripheral
准确描述了“我有一个特质实现,但我没有说哪个”的情况:
// This should NOT be in an `impl<PeripheralType>` block.
fn get_device() -> impl Peripheral {
let central = Manager::new()
.unwrap()
.adapters()
.unwrap()
.into_iter()
.next()
.unwrap()
.connect()
.unwrap();
central
.peripheral(BDAddr::from_str("2A:00:AA:BB:CC:DD").unwrap())
.unwrap()
}
然后使用它,您可以使用此返回值构造您的结构。
fn main() {
let device = get_device();
let my_struct = MyStruct { device };
my.do_something();
}
但是,有一个陷阱:您永远无法写下my_struct
的类型,因为它包含一个无法命名的参数。如果您需要这样做,那么我认为您将不得不使用动态调度:
struct MyStruct {
device: Box<dyn Peripheral>,
}
使用这种类型,没有类型参数会给您带来麻烦。 (您需要编写Box::new(central...unwrap())
来初始化struct字段。)要注意的是,将device
传递给期望某些外围设备类型的东西将不起作用。 / p>
在某种程度上work internally似乎如此,但是我不明白为什么在我的示例中它不起作用...
该代码有效,因为它是完全通用的;它没有get_device
试图使外围设备类型比“我的类型参数是什么”更具体。
无论您如何尝试实现此功能,都无法使用:
impl<T: SomeTrait> ImplementorManager<T> {
...
pub fn new_implementor(first: bool, name: &str) -> T {
match first {
true => Implementor1(...),
false => Implementor2 {...},
}
}
}
当您在-> T
中写入impl<T: SomeTrait>
时,是说对于实现T
的所有T
,此方法将始终返回SomeTrait
。 / em>但这不是您在做什么;您将返回两个不同的特定类型,这些类型不保证等于T
。
这里的基本问题是您当前正在尝试基于值(T
)选择类型参数(first
),这是不可能的。解决方案是使用静态类型信息,您可以通过编写自己的特征和实现来实现:
trait SomeTraitFactory: SomeTrait {
fn new(name: &str) -> Self;
}
impl SomeTraitFactory for Implementor1 {
fn new(name: &str) -> Self {
Implementor1(name.to_string())
}
}
impl SomeTraitFactory for Implementor2 {
fn new(name: &str) -> Self {
Implementor2 {
name: name.to_string(),
}
}
}
一旦有了这个工厂,就可以让ImplementorManager
在任何需要的地方使用它:
impl<T: SomeTraitFactory> ImplementorManager<T> {
...
pub fn new_implementor(name: &str) -> T {
<T as SomeTraitFactory>::new(name)
}
}
请注意,bool
参数已消失,因为您使用的ImplementorManager
的类型完全决定了构造哪个实现器。但是,调用new_implementor
有点烦人,因为您需要写出type参数:
<ImplementorManager<Implementor2>>::new_implementor("second")
当您在使用ImplementorManager
的方法中实际开始使用self
值时,此问题就消失了,因为可以使用Self
来携带类型:
impl<T: SomeTraitFactory> ImplementorManager<T> {
...
pub fn push_implementor(&mut self, name: &str) {
self.implementors.push(Self::new_implementor(name));
}
}
另一方面,如果您实际上想将Implementor1
和Implementor2
放在同一个ImplementorManager
中,那么所有<T>
都是多余的,则需要使用而是使用Box<dyn Trait>
方法。这不会直接起作用,因为SomeTrait: Clone
和Clone
不是对象安全的,但是您可以添加一个包装特征,该特征转发到SomeTrait
但隐藏Clone
部分:< / p>
trait SomeTraitWrapper: Debug {
fn get_name(&self) -> &str;
}
impl<T: SomeTrait> SomeTraitWrapper for T {
fn get_name(&self) -> &str {
SomeTrait::get_name(self)
}
}
然后ImplementorManager
是dyn
的直接用法:
struct ImplementorManager {
implementors: Vec<Box<dyn SomeTraitWrapper>>,
}
impl ImplementorManager {
pub fn call_get_name(implementor: Box<dyn SomeTraitWrapper>) -> String {
implementor.get_name().to_string()
}
pub fn new_implementor(first: bool, name: &str) -> Box<dyn SomeTraitWrapper> {
match first {
true => Box::new(Implementor1(name.to_string())),
false => Box::new(Implementor2 {
name: name.to_string(),
}),
}
}
}
答案 1 :(得分:0)
使用new_implementor
作为每个对象实现的特征:
fn new_implementor<U: SomeTrait>(x: U) -> U
where
U: DoSomething,
{
x.do_something()
}
所有内容都将如下所示:
use std::fmt::Debug;
pub trait SomeTrait: Clone + Debug {
fn get_name(&self) -> &str;
}
#[derive(Clone, Debug)]
struct Implementor1(String);
impl Implementor1 {
fn new(a: &str) -> Implementor1 {
Self(a.to_string())
}
}
impl SomeTrait for Implementor1 {
fn get_name(&self) -> &str {
&self.0
}
}
#[derive(Clone, Debug)]
struct Implementor2 {
name: String,
}
impl SomeTrait for Implementor2 {
fn get_name(&self) -> &str {
&self.name
}
}
trait DoSomething {
fn do_something(&self) -> Self
where
Self: SomeTrait;
// T: SomeTrait;
}
impl DoSomething for Implementor1 {
fn do_something(&self) -> Implementor1 {
Implementor1::new(&self.0)
}
}
impl DoSomething for Implementor2 {
fn do_something(&self) -> Implementor2 {
Self {
name: self.name.to_string(),
}
}
}
// the code below is mine
struct ImplementorManager<T: SomeTrait> {
implementors: Vec<T>,
}
impl<T: SomeTrait> ImplementorManager<T> {
pub fn call_get_name(implementor: T) -> String {
implementor.get_name().to_string()
}
fn new_implementor<U: SomeTrait>(x: U) -> U
where
U: DoSomething,
{
x.do_something()
}
}
fn main() {
let implementor2 = Implementor2 {
name: "test".to_string(),
};
let implementor1 = Implementor1("test".to_string());
println!(
"name: {:?}",
ImplementorManager::<Implementor2>::new_implementor(implementor2)
);
println!(
"name: {:?}",
ImplementorManager::<Implementor1>::new_implementor(implementor1)
);
}