在 HashMap 中存储闭包 [英] Storing a closure in a HashMap

查看:69
本文介绍了在 HashMap 中存储闭包的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

为了学习 Rust 语言,我使用了一个旧的 C++ 库并试图将它转换为 Rust.它使用了很多 C++11 闭包,我在翻译概念时遇到了一些困难.

To learn the Rust language, I'm taking an old C++ library I had lying around and trying to convert it to Rust. It used a lot of C++11 closures and I'm having some difficulty getting the concepts to translate.

在 C++ 中,我有这样的事情:

In C++ I had something like this:

// library.h
struct Event {
    // just some data
};

class Object {
public:
    // ...
    std::function<void(Event&)>& makeFunc(std::string& s) {
        return m_funcs[s];
    }
    // ...
private:
    // ...
    std::map<std::string, std::function<void(Event&)>> m_funcs;
    // ...
};

// main.cpp using the library
int main()
{
    Object foo;
    foo.makeFunc("func1") = [&]{
        // do stuff
    };
    return 0;
}

我遇到问题的部分是将函数正确存储在 Rust HashMap 集合中.我试过这个:

The part that I'm having trouble with is properly storing the functions in a Rust HashMap collection. I tried this:

struct Event;

struct Object {
    m_funcs : HashMap<String, FnMut(&Event)>
}

impl Object {
    // send f as another parameter rather than try and return borrow
    // compiler was complaining
    fn makeFunc(&mut self, s : &str,f: FnMut(&Event)) {
        self.m_funcs.insert(String::from_str(s), f); 
    }
}

但它说特征 core::marker::Sized 没有为类型 'for('r) core::ops::FnMut(&'r CreateEvent)' 实现

这是有道理的,因为 FnMut 是一个特征,因此在编译时没有已知的 HashMap 大小.所以我认为 hashmap 需要一个实际的指针而不是一个抽象类型.所以我改成这个

This makes sense because FnMut is a trait, and therefore has no known size for the HashMap to make at compile time. So I figure that the hashmap would require an actual pointer rather than an abstract type. So I change it to this

struct Object {
    m_funcs : HashMap<String, Box<FnMut(&Event)>>
}

impl Object {
    fn makeFunc(&mut self, s : &str, f: &FnMut(&Event)) {
        self.m_funcs.insert(String::from_str(s), Box::new(f));
    }
}

现在它说特征'for('r) core::ops::Fn<(&'r CreateEvent,)>'未针对插入中的类型 '&for('r) core::ops::FnMut(&'r CreateEvent)' [E0277] 实现.这个错误对我来说毫无意义.有人可以向我解释在 HashMap 中存储对非转义闭包的引用的正确方法吗?

now it says the trait 'for('r) core::ops::Fn<(&'r CreateEvent,)>' is not implemented for the type '&for('r) core::ops::FnMut(&'r CreateEvent)' [E0277] at the insert. This error makes no sense to me at all. Can someone explain to me the proper way to store a reference to a non-escaping closure in a HashMap?

推荐答案

你已经获取了一个 &FnMut(&Event)——一个 trait 对象——并且在装箱之后希望存储它作为一个 Box.因此,您要求 &FnMut(&Event) 必须实现 FnMut(&Event),而它没有(并且显然不能,对于 FnMut.call_mut 接受 &mut self).

You have taken a &FnMut(&Event)—a trait object—and, after boxing it, wish to store it as a Box<FnMut(&Event)>. Thus, you require that &FnMut(&Event) must implement FnMut(&Event), which it does not (and clearly cannot, for FnMut.call_mut takes &mut self).

您想要的是采用实现 FnMut(&Event) 的任意类型——即使用泛型——并按值获取它.签名是这样的:

What you wanted was to take an arbitrary type that implements FnMut(&Event)—that is, use generics—and take it by value. The signature is thus this:

fn make_func<F: FnMut(&Event)>(&mut self, s: &str, f: F)

但是,由于生命周期的原因,它会变得比这更复杂一些,但是您希望对此做的事情可能会有所不同;将带有引用 arg 的未装箱闭包存储在一个HashMap 有关于该主题的更多信息.以下是我认为您最有可能想要的:

It gets a little more complex than this due to lifetimes, however, but what you wish to do with regards to that may vary; Storing an unboxed closure with a reference arg in a HashMap has more information on that topic. Here’s what I believe you’re most likely to want:

struct Object<'a> {
    m_funcs: HashMap<String, Box<FnMut(&Event) + 'a>>,
}

impl<'a> Object<'a> {
    fn make_func<F: FnMut(&Event) + 'a>(&mut self, s: &str, f: F) {
        self.m_funcs.insert(String::from_str(s), Box::new(f));
    }
}

您可以删除所有 'a 以支持仅绑定在 F 上的单个 + 'static 如果您愿意不让任何闭包都捕获对其环境的引用.

You could remove all the 'a in favour of just a single + 'static bound on F if you are happy to not let any of the closures capture references to their environments.

这篇关于在 HashMap 中存储闭包的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆