gpt4 book ai didi

rust - 从方法返回实现 trait 的对象

转载 作者:行者123 更新时间:2023-12-03 11:36:56 25 4
gpt4 key购买 nike

我在外部 crate 中定义了一个特征,我需要从我定义的结构中的方法中返回它。接受 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 上,此结构是公共(public)的,但在私有(private)模块中。只有 Peripheral trait 暴露在 api 模块中。
这是一个示例代码:
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 ,但我不明白为什么它在我的例子中不起作用......

最佳答案

在这段代码中:

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> 的意思),然后尝试为其使用特定但无法命名的类型的值。
(旁注:在 Rust 中使用闭包时也会出现不可命名的类型——每个闭包定义都有一个唯一的不可命名类型——所以这不是一个不寻常的问题。)
相反,您需要做的是首先获取值,然后为它创建结构。首先,这是 get_device 的定义。应该可以,因为 impl Peripheral准确描述了“我有一个 trait 实现,但我没有说是哪一个”的情况:
// 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期望某种外围类型的东西不起作用。

It somehow seems to work internally, but I don't understand why it doesn't work in my example...


该代码有效,因为它是完全通用的;它没有 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 的.但这不是你正在做的;您将返回两种不同的特定类型,它们不能保证等于 T .
这里的基本问题是您当前正试图根据值( T )选择类型参数( first ),这是不可能的。解决方案是使用静态类型信息,您可以通过编写自己的 trait 和实现来做到这一点:
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有点烦,但是,因为您需要写出类型参数:
<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));
}
}

另一方面,如果你真的想要 Implementor1Implementor2在同一个 ImplementorManager ,然后所有 <T> s 是不需要的,您需要使用 Box<dyn Trait>而是接近。这不会直接起作用,因为 SomeTrait: CloneClone不是对象安全的,但您可以添加一个包装器特征,它转发到 SomeTrait但隐藏了 Clone部分:
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(),
}),
}
}
}

关于rust - 从方法返回实现 trait 的对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64181613/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com