这是我已经纠结了一段时间的代码

use ethers_providers::{Provider, Http}; //, JsonRpcClient};

// this doesn't work
// trait ClientGeneric<T> {
//     fn get_client(&self, url:&str) -> Provider<T> {
//         Provider::<T>::try_from(url).unwrap()
//     }
// }
// this works but I want generic
trait ClientConcrete {
    fn get_client(&self, url:&str) -> Provider<Http> {
        Provider::<Http>::try_from(url).unwrap()
    }
}
struct Test;
impl ClientConcrete for Test {}

#[cfg( test )]
mod tests {
    use super::*;
    // #[test]
    // fn test_get_client_generic() {
    //     let test = Test{};
    //     let client = test.get_client("http://localhost:8080");
    // }
    #[test]
    fn test_get_client_concrete() {
        let test = Test{};
        let client = test.get_client("http://localhost:8080");
    }
}

我希望 for each 传输T返回Provider<T>:http、ws、ipc、mock等 通常,每个传输都实现了TryFrom特征.然而,上面的代码不适用于泛型T,即使它适用于具体的传输.对于泛型T,我收到的错误:

error[E0277]: the trait bound `Provider<T>: From<&str>` is not satisfied
 --> src/test.rs:6:9
  |
6 |         Provider::<T>::try_from(url).unwrap()
  |         ^^^^^^^^^^^^^^^^^^^^^^^ the trait `From<&str>` is not implemented for `Provider<T>`
  |
  = help: the following other types implement trait `TryFrom<T>`:
            <Provider<Http> as TryFrom<&'a String>>
            <Provider<Http> as TryFrom<&str>>
            <Provider<Http> as TryFrom<String>>
  = note: required because of the requirements on the impl of `Into<Provider<T>>` for `&str`
  = note: required because of the requirements on the impl of `TryFrom<&str>` for `Provider<T>`

不幸的是,在类型T上没有匹配语句,我可以使用(就像在Golang中一样)来实现具体的类型. 据我所知,我不能为外来 struct Provider<T>实现外来特性TryFrom<&str>.TryFrom<&str>是为基础类型HttpWsIpc等实现的

还有什么我能做的吗?

推荐答案

以下是不能编译的泛型函数:

fn get_client(&self, url:&str) -> Provider<T> {
    Provider::<T>::try_from(url).unwrap()
}

它不编译的原因是T没有指定任何边界.如果编译完成,这将意味着该函数可以为any T创建一个Provider<T>.但这是不可能的;正如您注意到的,只有某些类型,如Http,才能为T工作,因为Provider<Http>实现了TryFrom<&str>.因此,您需要提供这些必要的界限,以便函数可以编译.

我是这样写的:

use std::fmt::Debug;

use ethers_providers::Provider;

trait ClientGeneric<T>
where
    for<'a> &'a str: TryInto<Provider<T>>,
    for<'a> <&'a str as TryInto<Provider<T>>>::Error: Debug,
{
    fn get_client(&self, url: &str) -> Provider<T> {
        url.try_into().unwrap()
    }
}

impl<T, U> ClientGeneric<U> for T
where
    for<'a> &'a str: TryInto<Provider<U>>,
    for<'a> <&'a str as TryInto<Provider<U>>>::Error: Debug,
{
}

struct Test;

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_get_client_generic() {
        let test = Test {};
        let _client = test.get_client("http://localhost:8080");
    }
}

注我 Select 使用TryInto而不是TryFrom,尽管在这种情况下两者都可以很好地工作.

第一个绑定允许我们调用try_into()&str转换为(Result包装的)Provider<T>:

for<'a> &'a str: TryInto<Provider<T>>

第二个界限是需要的,因此.unwrap()方法有效:

for<'a> <&'a str as TryInto<Provider<T>>>::Error: Debug

但在实际代码中,您可能应该避免展开,而应返回Result.

Rust相关问答推荐

如何使用 list 在Rust for Windows中编译?

Box::new()会从一个堆栈复制到另一个堆吗?

在Rust中,如果Result是Err,运行副作用(如日志(log)记录)的惯用方法是什么

如何修复数组中NewType导致的运行时开销

`RwLockWriteGuard_,T`不实现T实现的特征

Rust函数的返回值不能引用局部变量或临时变量

用于实现获取 struct 体 id 的特征规范

为什么`tokio::main`可以直接使用而不需要任何导入?

为什么需要静态生命周期以及在处理 Rust 迭代器时如何缩小它?

为什么需要同时为值和引用实现`From`?方法不应该自动解引用或borrow 吗?(2023-06-16)

Rust中如何实现一个与Sized相反的负特性(Unsized)

Rust:`sort_by` 多个条件,冗长的模式匹配

仅当函数写为闭包时才会出现生命周期错误

如何在 Rust 中返回通用 struct

在 RefCell 上borrow

如何将切片推入数组?

Rust 生命周期:不能在方法内重新borrow 可变字段

为什么 Rust 标准库同时为 Thing 和 &Thing 实现特征?

类型参数不受 impl 特征、自身类型或谓词的约束

为什么可以从不可变 struct 的字段中移动?