rust 不匹配的类型:一种类型比另一种更一般,使用受约束的高阶性状界限

6rqinv9w  于 2023-08-05  发布在  其他
关注(0)|答案(1)|浏览(122)

这是"dropped here while still borrowed" when making lifetime explicits的后续,但可以独立查看。
既然@jthulhu让我发现了修复我之前错误的高阶特质界限,我想更进一步推广它。让我们创建一个trait:

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

字符串
这个trait可以毫无问题地实现,并且当直接使用时,可以按照预期工作,如这个测试所示:

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_something的函数,不知道实现者,但知道Arg类型的一些约束,以便我们可以使用它。

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章节,并认为Borrow splitting会给我一些答案,并查看了Take的实现,但没有更高级别的特质界限。

ej83mcc0

ej83mcc01#

经过大量的挖掘,我最终在RFC中找到了一个解决方案:https://github.com/rust-lang/rfcs/blob/master/text/1598-generic_associated_types.md#using-associated-type-constructors-in-bounds
诀窍是将Arg的生存期与ToImplement分开声明,如下所示:

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);
    }

相关问题