如何为具有闭包成员的结构体创建新的关联函数?

5

我几乎可以直觉地感觉到为什么这段代码不会工作,但我无法确切地指出原因。我认为这与新函数每次都具有不同的返回类型有关。

为什么这是个问题?为什么直接创建可以工作?

struct Struct<T>
where
    T: Fn(&[u8]),
{
    func: T,
}

impl<T> Struct<T>
where
    T: Fn(&[u8]),
{
    fn new() -> Struct<T> {
        // this doesn't work
        Struct { func: |msg| {} }
    }
}

fn main() {
    // this works
    let s = Struct { func: |msg| {} };
}

错误是:
error[E0308]: mismatched types
  --> src/main.rs:14:24
   |
14 |         Struct { func: |msg| {} }
   |                        ^^^^^^^^ expected type parameter, found closure
   |
   = note: expected type `T`
              found type `[closure@src/main.rs:14:24: 14:32]`

我认为这与新函数每次具有不同的返回类型有关。非常接近!要问的问题是“谁决定了类型T是什么?”你希望new的实现决定返回哪个闭包,但是泛型让调用者决定类型T是什么。我相信还有另一个问题解决了这个问题...我会找一下。 - trent
好的,我认为这很有道理。基本上,如果新函数起作用,类型将自行决定T是什么,而不是调用代码决定? - anderspitman
是的!但有时这也是一件有用的事情,你可以使用impl Trait来实现。什么是正确的返回迭代器(或任何其他trait)的方法? 是我在寻找的另一个问题。只需将 fn new() -> Struct<T> 更改为 fn new() -> Struct<impl Fn(&[u8])> 即可解决问题。 - trent
好的,接近了。但是,在你的更改之后,我仍然无法调用新函数。它会显示“需要类型注释:无法解析 for<'r> <_ as std::ops::FnOnce<(&'r [u8],)>>::Output == ()”。 - anderspitman
另一个答案提供了很多信息,但我不确定它是否适用于这里。我特别想在另一个类型内设置一个类型,而不是尝试返回一个类型。虽然它们都紧密相连... - anderspitman
让我们在聊天中继续这个讨论 - trent
1个回答

5

简而言之; 您可以执行以下操作:

fn new() -> Struct<impl Fn(&[u8])> {
    Struct { func: |msg| {} }
}

更详细的解释如下:
让我们剖析你的 "impl" 代码块:
impl<T> Struct<T>
where
    T: Fn(&[u8]),
{
    fn new() -> Struct<T> {
        // this doesn't work
        Struct { func: |msg| {} }
    }
}

我们从以下内容开始:
impl<T> Struct<T>
where
    T: Fn(&[u8]),

这告诉编译器整个impl块对于满足Fn(&[u8])的任何T都是“有效”的。现在:
fn new() -> Struct<T> {
    // this doesn't work
    Struct { func: |msg| {} }
}

你说new返回一个Struct<T>,我们现在的代码块说明它适用于满足Fn(&[u8])任何T。然而,你返回Struct一个特定实例,即由|msg| {}参数化的那个 - 因此,返回值不能是任何T满足Fn(&[u8])Struct<T>
但是,你可以进行以下修改:
fn new() -> Struct<impl Fn(&[u8])> {
    Struct { func: |msg| {} }
}

这告诉编译器new返回一个Struct,其参数已知满足Fn(&[u8]),因此编译器应该推断它。特别地,它对T没有任何假设,并返回一个特定的类型

然而,在直接初始化中,我们告诉编译器:

let s = Struct { func: |msg| {} };

编译器看到你想创建一个结构体,并且知道为了创建它,必须为T res.函数推断类型。它看到你将|msg| {}传递给函数,推断闭包的类型,现在知道了可以放入T中的具体类型。

感谢您的详细解释。表面上看,这一切都很有道理,但是当我尝试使用这段代码时,我发现您所做的更改可以使代码编译通过,但是却无法让我使用默认闭包的Struct::new()。当我尝试时,会出现错误https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=195e4f2be90803d86a83d4b12a6c66a3。错误信息为“cannot satisfy for<'r> <_ as FnOnce<(&'r [u8],)>>::Output == ()”。 - Schneems

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接