不匹配的类型:一种类型比另一种类型更通用,使用受约束的高等级特征边界

mismatched types : one type is more general than the other, using constrained Higher-Rank Trait Bounds

提问人:Xavier Detant 提问时间:7/7/2023 更新时间:7/8/2023 访问量:29

问:

这是“在借用时放在这里”的后续,在进行生命周期明确时,但可以独立查看。

既然@jthulhu让我发现了修复我之前错误的高级特征边界,我想通过概括它来更进一步。因此,让我们创建一个特征:

trait ToImplement {
    type Arg<'arg>;
    fn do_something<F>(&self, f: F)
    where
        F: for<'b> FnOnce(Self::Arg<'b>) -> ();
}

此特性可以毫无问题地实现,并且当直接使用时,可以按预期工作,如以下测试所示:

    impl ToImplement for String {
        type Arg<'arg> = &'arg str;
        fn do_something<F, T>(&self, f: F) -> T
        where
            F: for<'b> FnOnce(Self::Arg<'b>) -> T,
        {
            f(&self)
        }
    }

    #[test]
    fn works() {
        let hello = String::from("Hello");
        let r = hello.do_something(|s| format!("{s} world!"));
        assert_eq!(r,"Hello world!")
    }

现在,让我们尝试编写一个函数,该函数在不知道实现者的情况下使用该方法,但知道对类型的一些约束,以便我们可以使用它。do_somethingArg

  10   │ fn go<D, I>(implementor: I)
  11   │ where
  12   │     D: Display,
  13   │     for<'a> I: ToImplement<Arg<'a> = D>,
  14   │ {
  15   │     implementor.do_something(|a| println!("{a}"));
  16   │ }

这确实可以正确编译,但如果我们尝试像这样使用它:

  33   │     #[test]
  34   │     fn works() {
  35   │         let hello = String::from("Hello");
  36   │         go(hello);
  37   │     }

然后我们得到这个错误:

error[E0308]: mismatched types
   |
36 |         go(hello);
   |         ^^^^^^^^^ one type is more general than the other
   |
   = note: expected reference `&'a str`
              found reference `&str`
note: the lifetime requirement is introduced here
   |
13 |     for<'a> I: ToImplement<Arg<'a> = D>,
   |   

我认为我在第 13 行声明寿命的方式是错误的。但我不知道我还能怎么做。'a

我阅读了@jthulhu指出的关于所有权的rustonomicon章节,并认为借用拆分会给我一些答案,并查看了Take的实现,但没有更高等级的特征边界。

Rust Closures Lifetime 借用检查器

评论

0赞 Community 7/9/2023
请澄清您的具体问题或提供其他详细信息,以准确说明您的需求。正如目前所写的那样,很难确切地说出你在问什么。

答:

1赞 Xavier Detant 7/8/2023 #1

经过大量挖掘,我最终在RFC中找到了一个解决方案:https://github.com/rust-lang/rfcs/blob/master/text/1598-generic_associated_types.md#using-associated-type-constructors-in-bounds

诀窍是像这样单独声明生存期:ArgToImplement

fn go<I>(implementor: I)
where
    I: ToImplement,
    for<'a> I::Arg<'a>: Display,
{
    implementor.do_something(|a| println!("{a}"));
}

现在,它看起来像编译器作为推断类型的问题,因此我们必须在调用函数时定义它:

    #[test]
    fn works() {
        let hello = String::from("Hello");
        go::<String>(hello);
    }