从通用结构的方法返回封闭

发布于 2025-02-13 18:22:38 字数 2623 浏览 1 评论 0原文

我是一个有生锈的新手,在处理封闭时,我会遇到一些障碍,无论是从功能和 /或方法中返回时,要么是我需要将其存储为结构字段时。

让我们从有效的内容开始:

fn build_func(b: usize) -> impl Fn(usize) -> usize {
    move |a| a + b
}

struct Foo<F>
where
    F: Fn(usize) -> usize,
{
    pub foo: F,
}

impl<F> Foo<F>
where
    F: Fn(usize) -> usize,
{
    fn new(foo: F) -> Self {
        Self { foo }
    }
}

fn main() {
    let foo1 = Foo { foo: |a| a + 1 };

    let foo2 = Foo { foo: build_func(2) };

    let foo_func = build_func(3);
    let foo3 = Foo { foo: foo_func };
}

这是根据预期的工作,以及在结构之外构建的闭合类型与foo的通用匹配。

我想实现同样的方法,但是只需将其移入> foo本身的impl中,就可以隐藏闭合的创建。

我测试了这些替代方案,但没有任何主题编译:

struct Foo2<F>
where
    F: Fn(usize) -> usize,
{
    pub foo: F,
}

impl<F> Foo2<F>
where
    F: Fn(usize) -> usize,
{
    fn new() -> Self {
        let foo = build_func(1);
        Self { foo }
    }
}

struct Foo3<F>
where
    F: Fn(usize) -> usize,
{
    pub foo: F,
}

impl<F> Foo3<F>
where
    F: Fn(usize) -> usize,
{
    fn new() -> Self {
        let foo = |a| a + 1;
        Self { foo }
    }
}

struct Foo4<F>
where
    F: Fn(usize) -> usize,
{
    pub foo: F,
}

impl<F> Foo4<F>
where
    F: Fn(usize) -> usize,
{
    fn new() -> Self {
        let foo = Self::build_func(1);
        Self { foo }
    }

    fn build_func(b: usize) -> F {
        move |a| a + b
    }
}

struct Foo5<F>
where
    F: Fn(usize) -> usize,
{
    pub foo: F,
}

impl<F> Foo5<F>
where
    F: Fn(usize) -> usize,
{
    fn new() -> Self {
        let foo = Self::build_func(1);
        Self { foo }
    }

    fn build_func(b: usize) -> impl Fn(usize) -> usize {
        move |a| a + b
    }
}

我知道每个封闭都有其自己的不透明和独特的类型,但是我不明白为什么为什么最初的实现foo正常工作。 。

通过阅读接受的答案在这里我似乎知道唯一的选择,在这种情况是要封闭封闭,但我仍然没有充分的理解。

通过将盒装闭合和特质别名结合起来(我知道这不是“真实的”特质别名),我想到了:

trait Func: Fn(usize) -> usize {}
impl<T> Func for T where T: Fn(usize) -> usize {}

struct Foo6 {
    pub foo: Box<dyn Func>,
}

impl Foo6 {
    fn new() -> Self {
        let foo = Self::build_func(1);
        Self { foo: Box::new(foo) }
    }

    fn build_func(b: usize) -> impl Func {
        move |a| a + b
    }
}

fn main() {
    let foo = Foo6::new();
    println!("{}", (foo.foo)(1));
}

但是我想知道是否有可能获得未包装的版本。

I'm a newbie with Rust and I bumped into some obstacles when dealing with closures, either when returning them from functions and or methods, either when I need to store them as struct fields.

Let's start with what is working:

fn build_func(b: usize) -> impl Fn(usize) -> usize {
    move |a| a + b
}

struct Foo<F>
where
    F: Fn(usize) -> usize,
{
    pub foo: F,
}

impl<F> Foo<F>
where
    F: Fn(usize) -> usize,
{
    fn new(foo: F) -> Self {
        Self { foo }
    }
}

fn main() {
    let foo1 = Foo { foo: |a| a + 1 };

    let foo2 = Foo { foo: build_func(2) };

    let foo_func = build_func(3);
    let foo3 = Foo { foo: foo_func };
}

This works as expected and the type of the closure that is built outside the struct is properly matched with the generic of Foo.

I wanted to achieve the same, but by hiding the creation of the closure simply by moving it inside the impl of Foo itself.

I tested these alternatives, but none of theme compiles:

struct Foo2<F>
where
    F: Fn(usize) -> usize,
{
    pub foo: F,
}

impl<F> Foo2<F>
where
    F: Fn(usize) -> usize,
{
    fn new() -> Self {
        let foo = build_func(1);
        Self { foo }
    }
}

struct Foo3<F>
where
    F: Fn(usize) -> usize,
{
    pub foo: F,
}

impl<F> Foo3<F>
where
    F: Fn(usize) -> usize,
{
    fn new() -> Self {
        let foo = |a| a + 1;
        Self { foo }
    }
}

struct Foo4<F>
where
    F: Fn(usize) -> usize,
{
    pub foo: F,
}

impl<F> Foo4<F>
where
    F: Fn(usize) -> usize,
{
    fn new() -> Self {
        let foo = Self::build_func(1);
        Self { foo }
    }

    fn build_func(b: usize) -> F {
        move |a| a + b
    }
}

struct Foo5<F>
where
    F: Fn(usize) -> usize,
{
    pub foo: F,
}

impl<F> Foo5<F>
where
    F: Fn(usize) -> usize,
{
    fn new() -> Self {
        let foo = Self::build_func(1);
        Self { foo }
    }

    fn build_func(b: usize) -> impl Fn(usize) -> usize {
        move |a| a + b
    }
}

I understand that each closure has its own opaque and distinct type, but then I don't understand why on the other hand the initial implementation of Foo works then.

By reading the accepted answer here I seem to understand that the only option, in this case, would be to box the closure, but I still don't have a full understanding.

By combining boxed closure and trait aliases (I know it's not the "real" trait aliasing) I came up with this:

trait Func: Fn(usize) -> usize {}
impl<T> Func for T where T: Fn(usize) -> usize {}

struct Foo6 {
    pub foo: Box<dyn Func>,
}

impl Foo6 {
    fn new() -> Self {
        let foo = Self::build_func(1);
        Self { foo: Box::new(foo) }
    }

    fn build_func(b: usize) -> impl Func {
        move |a| a + b
    }
}

fn main() {
    let foo = Foo6::new();
    println!("{}", (foo.foo)(1));
}

But I'm wondering whether it's possible to obtain an unboxed version.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

毁虫ゝ 2025-02-20 18:22:38

此代码中的问题

impl<F> Foo2<F>
where
    F: Fn(usize) -> usize,
{
    fn new() -> Self {
        let foo = build_func(1);
        Self { foo }
    }
}

是:要称呼它,用户代码需要编写类似的内容:

let foo2 = <Foo2<?>>::new();

并在其中指定某种类型。即使未明确键入类型,也应在此解决(类型遗漏主要是句法糖)。但是,在调用build_func(1)的调用中,存储值的类型是在该函数内部确定的,因此用户无需使用类型,并且该函数无法调用。

我的建议是仅写一个免费的函数:

fn new_foo2() -> Foo2<impl Fn(usize) -> usize> {
    let foo = build_func(1);
    Foo2 { foo }
}

现在,通用类型是函数的返回类型中的impl,这是由函数代码决定的一种特殊的不透明通用。所以这有效。


如果您真的,真的想编写foo2 :: new可以在虚拟的非生成inmply foo2 block中编写功能实现。通常,这是inmand foo2&lt;()&gt;,但是类型()不满足您的约束,但是您可以使用任何其他虚拟类型:(

impl Foo2<fn(usize)->usize> {
    fn new() -> Foo2<impl Fn(usize) -> usize> {
        let foo = build_func(1);
        Foo2 { foo }
    }
}

请注意,请注意此new()不返回self,因为通用类型不正确。)

现在您至少可以写下:

let foo2 = Foo2::new();

The problem in this code:

impl<F> Foo2<F>
where
    F: Fn(usize) -> usize,
{
    fn new() -> Self {
        let foo = build_func(1);
        Self { foo }
    }
}

is that to call it, the user code would need to write something like this:

let foo2 = <Foo2<?>>::new();

And specify some type there. Even if the type is not typed explicitly, it should be resolved there (type omission is mostly syntactic sugar). But the type of the stored value is decided inside that function, in the call to build_func(1), so the user has no type to use there and that function cannot be called.

My advice is to just write a free function:

fn new_foo2() -> Foo2<impl Fn(usize) -> usize> {
    let foo = build_func(1);
    Foo2 { foo }
}

Now the generic type is an impl in the return type of the function, that is an special opaque generic decided by the code of the function. So this works.


If you really, really want to write Foo2::new you can write the function implementation inside a dummy non-generic impl Foo2 block. Usually that would be impl Foo2<()> but the type () does not satisfies your constraints, but you can use any other dummy type that does:

impl Foo2<fn(usize)->usize> {
    fn new() -> Foo2<impl Fn(usize) -> usize> {
        let foo = build_func(1);
        Foo2 { foo }
    }
}

(Note that this new() does not return Self because the generic type is not correct.)

And now you can at least write:

let foo2 = Foo2::new();
娇妻 2025-02-20 18:22:38

这里的区别在于,您正在从new()中返回self。内部impl&lt; f&gt; foo&lt; f&gt;self是指foo&lt; f&gt;f是一个通用参数 - 您无法构建类型f的封闭,因为它是您的呼叫者决定它是什么的类型,而不是您。

您的第一个版本之所以有效,是因为它告诉编译器“我将返回某些类型实现fn;我将其留给您,从而推断出什么”。另一方面,第二个版本都是“给定任何 type f实现fn,我将为您提供该类型的实例” 。当然,这是不可能的。

相反,您也希望编译器在此处推断使用的类型。最好的解决方案也是在这里使用IMPH特征。但是IMPH特征在返回类型以外的其他位置不稳定。它看起来像():

#![feature(type_alias_impl_trait)]

type InnerFn = impl Fn(usize) -> usize;

struct Foo {
    pub foo: InnerFn,
}

impl Foo {
    fn new() -> Self {
        let foo = Self::build_func(1);
        Self { foo }
    }
    
    fn build_func(b: usize) -> InnerFn {
        move |a| a + b
    }
}

另一个(相当hacky)解决方案是具有通用参数,但不使用它,而是在new()中使用Impl特征。为了不需要呼叫者指定冗余参数(由于未使用而不能再推断出它),我们可以使用标记类型,通常()。这要求我们删除f:fn(usize) - &gt; usize bound from the struct and put it only on the impl, however

struct Foo<F> {
    pub foo: F,
}

impl Foo<()> {
    fn new() -> Foo<impl Fn(usize) -> usize> {
        let foo = Self::build_func(1);
        Foo { foo }
    }
    
    fn build_func(b: usize) -> impl Fn(usize) -> usize {
        move |a| a + b
    }
}

;确实要封闭封闭,但是您不需要新的特征 - 您可以直接使用fn游乐场):

struct Foo {
    pub foo: Box<dyn Fn(usize) -> usize>,
}

impl Foo {
    fn new() -> Self {
        let foo = Self::build_func(1);
        Self { foo: Box::new(foo) }
    }

    fn build_func(b: usize) -> impl Fn(usize) -> usize {
        move |a| a + b
    }
}

The difference here is that you're returning Self from new(). Inside impl<F> Foo<F>, Self refers to Foo<F>. And F is a generic parameter - you cannot build a closure of type F, because it's a type your caller decides what it is, not you.

Your first version works because it tells the compiler "I will return some type implementing Fn; I'll leave it to you infer what exactly". On the other hand, the second versions are all "given any type F implementing Fn, I'll give you an instance of that type". That is of course impossible.

Instead you want the compiler to infer the used type here, too. The best solution will be to use impl Trait here, too. But impl Trait in positions other than return type is unstable. It will look like (playground):

#![feature(type_alias_impl_trait)]

type InnerFn = impl Fn(usize) -> usize;

struct Foo {
    pub foo: InnerFn,
}

impl Foo {
    fn new() -> Self {
        let foo = Self::build_func(1);
        Self { foo }
    }
    
    fn build_func(b: usize) -> InnerFn {
        move |a| a + b
    }
}

Another (quite hacky) solution is to have a generic parameter, but not use it and instead use impl Trait in new(). To not require the caller to specify the redundant parameter (since it can't be inferred anymore as it's unused), we can use a marker type, usually (). This requires us to remove the F: Fn(usize) -> usize bound from the struct and put it only on the impl, however this is a good style anyway (playground):

struct Foo<F> {
    pub foo: F,
}

impl Foo<()> {
    fn new() -> Foo<impl Fn(usize) -> usize> {
        let foo = Self::build_func(1);
        Foo { foo }
    }
    
    fn build_func(b: usize) -> impl Fn(usize) -> usize {
        move |a| a + b
    }
}

The last solution is indeed to box the closure, but you don't need a new trait for that - you can use Fn directly (playground):

struct Foo {
    pub foo: Box<dyn Fn(usize) -> usize>,
}

impl Foo {
    fn new() -> Self {
        let foo = Self::build_func(1);
        Self { foo: Box::new(foo) }
    }

    fn build_func(b: usize) -> impl Fn(usize) -> usize {
        move |a| a + b
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文