将'&self'方法内的闭包添加到结构中的属性

时间:2018-10-27 20:17:58

标签: rust closures self lifetime json-rpc

请考虑以下示例代码:

data

具有“ rpc :: IoHandler :: add_method”的以下方法签名

#[macro_use]
extern crate serde_derive;
extern crate serde;
extern crate serde_json;

extern crate jsonrpc_core as rpc;

#[derive(Serialize, Deserialize)]
struct Test {
    var: u32,
}

struct TestRpc {
    test: Test,
    rpc_io_handler: rpc::IoHandler,
}

impl TestRpc {
    fn new() -> Self {
        let ret = Self {
            test: Test { var: 1 },
            rpc_io_handler: rpc::IoHandler::new(),
        };
        ret.register_rpc_methods();
        ret
    }

    fn register_rpc_methods(&self) {
        let get_var = |_params: rpc::Params| match rpc::to_value(&self.test) {
            Ok(x) => Ok(x),
            Err(_) => Err(rpc::Error::internal_error()),
        };
        self.rpc_io_handler.add_method("get_var", get_var);
    }

    fn get_var_test(&self, msg: &str) -> Option<String> {
        self.rpc_io_handler.handle_request_sync(msg)
    }
}

fn main() {
    let test = TestRpc::new();
    let request = r#"{"jsonrpc": "2.0", "method": "get_var", "id": 1}"#;
    let response = r#"{"jsonrpc":"2.0","result":{"var":1},"id":1}"#;
    assert_eq!(test.get_var_test(request), Some(response.to_owned()));
}

method is from jsonrpcRpcMethodSimple一样。

尝试编译此错误消息

pub fn add_method<F>(&mut self, name: &str, method: F)
where
    F: RpcMethodSimple,

是否可以使用此方法(error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements --> src/main.rs:26:27 | 26 | let mut get_var = |_params: rpc::Params | { | ___________________________^ 27 | | match rpc::to_value(&self.test) { 28 | | Ok(x) => Ok(x), 29 | | Err(_) => Err(rpc::Error::internal_error()) 30 | | } 31 | | }; | |_________^ | note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 25:5... --> src/main.rs:25:5 | 25 | / fn register_rpc_methods(&self) { 26 | | let mut get_var = |_params: rpc::Params | { 27 | | match rpc::to_value(&self.test) { 28 | | Ok(x) => Ok(x), ... | 32 | | self.rpc_io_handler.add_method("get_var", get_var); 33 | | } | |_____^ = note: ...so that the types are compatible: expected &&TestRpc found &&TestRpc = note: but, the lifetime must be valid for the static lifetime... note: ...so that the type `[closure@src/main.rs:26:27: 31:10 self:&&TestRpc]` will meet its required lifetime bounds --> src/main.rs:32:29 | 32 | self.rpc_io_handler.add_method("get_var", get_var); | ^^^^^^^^^^ )而不更改包装箱中的方法?我在Rust的一生中挣扎;有没有一种简单方法来限制封闭的寿命?

1 个答案:

答案 0 :(得分:1)

我不太了解jsonrpc的内部结构,但是jsonrpc库是使用Tokio以完全异步的方式实现的。尽管您调用了同步请求处理,但是在内部它仍然异步执行请求,并只是阻塞线程直到完成。缺点是Tokio无法保证在任务执行程序中安排关闭时间。因此,与任何self相比,任何此类闭包的生命周期都更多地与执行者联系在一起。

在上面的代码中,您捕获了对self的引用,但不能保证self在执行闭包时仍然有效。因此,您必须move使用闭包使用的任何数据。此外,闭包必须是Send才能与Tokio一起使用,因此您不能简单地使用Rc并将副本移动到闭包中。

对于您而言,我知道的最简单的方法是将test更改为键入Arc<Test>。然后更改闭包定义以将变量的副本移到闭包中。您还遇到了一些可变性问题,这是一个完整的示例,可以编译:

#[macro_use]
extern crate serde_derive;
extern crate serde;
extern crate serde_json;

extern crate jsonrpc_core as rpc;

use std::borrow::Borrow;
use std::sync::Arc;

#[derive(Serialize, Deserialize)]
struct Test {
    var: u32,
}

struct TestRpc {
    test: Arc<Test>,
    rpc_io_handler: rpc::IoHandler,
}

impl TestRpc {
    fn new() -> Self {
        let mut ret = Self {
            test: Arc::new(Test { var: 1 }),
            rpc_io_handler: rpc::IoHandler::new(),
        };
        ret.register_rpc_methods();
        ret
    }

    fn register_rpc_methods(&mut self) {
        let test_clone = self.test.clone();
        let get_var = move |_params: rpc::Params| match rpc::to_value(test_clone.borrow() as &Test)
        {
            Ok(x) => Ok(x),
            Err(_) => Err(rpc::Error::internal_error()),
        };
        self.rpc_io_handler.add_method("get_var", get_var);
    }

    fn get_var_test(&self, msg: &str) -> Option<String> {
        self.rpc_io_handler.handle_request_sync(msg)
    }
}

fn main() {
    let test = TestRpc::new();
    let request = r#"{"jsonrpc": "2.0", "method": "get_var", "id": 1}"#;
    let response = r#"{"jsonrpc":"2.0","result":{"var":1},"id":1}"#;
    assert_eq!(test.get_var_test(request), Some(response.to_owned()));
}