gpt4 book ai didi

generics - 在不固定 `Fn` 参数之一的情况下,在结构定义上指定 `Fn` trait bound

转载 作者:行者123 更新时间:2023-11-29 07:44:29 24 4
gpt4 key购买 nike

我有一个包含函数对象的结构:

struct Foo<F> {
func: F,
}

我想添加一个 Fn特征绑定(bind)到结构定义。问题是:我确实关心第一个参数(它必须是 i32 ),但不关心第二个参数。我真正想写的是这样的:

struct Foo<F> 
where
∃ P so that F: Fn(i32, P),
{
func: F,
}

所以在英语中:类型 F必须是一个有两个参数的函数,第一个参数是 i32 (第二个可以是任何东西)。上面的语法显然是无效的。我考虑了三种可能的解决方案:

  1. for<>语法在这里无济于事。除了它不适用于非生命周期参数之外,它是通用的(“对所有人”)而不是存在的(“存在”)。这样就结束了。

  2. 另一种可能性是向结构添加类型参数。我已经不喜欢那个解决方案了,因为参数本身并不属于结构。

    struct Foo<F, P> 
    where
    F: Fn(i32, P),
    {
    func: F,
    }

    但这不起作用:参数 P不使用,除了在where绑定(bind),所以编译器会提示。

    这个问题可以通过添加PhantomData<P>来解决字段,但这不是必需的,更重要的是,用户不能再轻松地使用结构构造函数语法。

  3. 最后我尝试了这个:

    struct Foo<F> 
    where
    F: Fn(i32, _),
    {
    func: F,
    }

    但这也行不通:

    error[E0121]: the type placeholder `_` is not allowed within types on item signatures
    --> src/main.rs:3:20
    |
    3 | F: Fn(i32, _),
    | ^ not allowed in type signatures

有没有办法实现我想要的?


旁注:为什么我要将 trait 绑定(bind)到结构上,而不仅仅是 impl在重要的地方阻塞?

首先,一旦实现了“隐含特征边界”RFC,这就允许我从所有 impl 中省略重复的特征边界。 block 。其次,有了这个绑定(bind),它可以帮助编译器进行类型推断。考虑一下:

struct Foo<F, T> 
where
F: Fn(T, _),
{
data: T,
F: F,
}

如果绑定(bind)是可能的(我用上面的 PhantomData “解决方案”试过),编译器可以更容易地推断出闭包第一个参数的类型。如果仅在 impl block 上指定特征边界,编译器就会遇到困难。

最佳答案

与其对结构施加约束,最简单和最好的方法是对需要使用该函数的所有方法的实现施加约束:

struct Foo<F, T> {
data: T,
f: F,
}

impl<F, T> Foo<F, T> {
fn call_f<P>(&self, arg: P)
where
T: Copy,
F: Fn(T, P)
{
(self.f)(self.data, arg);
}
}

First, once the "implied trait bounds" RFC is implemented, this allows me to omit the duplicate trait bounds from all the impl blocks.

所以听起来您主要关心的是删除重复边界。如果这是问题所在,您可以尝试将具有相同边界的所有方法分组到一个通用的 impl 中,这样您仍然只需要编写一次:

impl<F, T, P> Foo<F, T> 
where
T: Copy,
F: Fn(T, P),
{
fn call_f(&self, arg: P) {
(self.f)(self.data, arg);
}
}

这里有一个小问题,类似于您自己发现的问题:unconstrained type parameter: P。然而,既然我们已经到了这里,您可以通过引入一个特征来非常简单地解决它(您可以根据您的特定用例为其命名):

trait FIsAFunction<F, T, P> {
fn call_f(&self, arg: P);
}

impl<F, T, P> FIsAFunction<F, T, P> for Foo<F, T>
where
T: Copy,
F: Fn(T, P),
{
fn call_f(&self, arg: P){
(self.f)(self.data, arg);
}
}

而且用户不必做任何奇怪的事情[1]:

fn main() {
fn callback(x: u32, y: &str) {
println!("I was given {:?} and {:?}", x, y)
}
let foo = Foo { data: 1u32, f: callback };
foo.call_f("hello!");
}

[1] 他们可能必须使用 trait。这不是那么奇怪的:你已经必须用很多std东西来做到这一点,比如std::io::Read等.

关于generics - 在不固定 `Fn` 参数之一的情况下,在结构定义上指定 `Fn` trait bound,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50671177/

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