< 返回版块

ZZG 发表于 2023-04-19 22:20

想练习一下Rust,尝试在struct Master里也建一个名叫work的的属性,类型为Option<Box<dyn FnMut(&Master)>>, 但是调用work(self)时有问题,想知道有没有办法把这段C++改写成Rust呢,谢谢

#include <vector>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <algorithm>
#include <set>
#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <functional>

using namespace std;

class Master {
public:
    std::function<void(Master*)> work{};
    int counter = 0;

    void register_work(std::function<void(Master*)> w) {
        work = w;
    }

    int increment() {
        counter += 1;
        return counter;
    }

    void run() {
        if (!work) {
            cout << "no work" << endl;
            return;
        }
        work(this);
    }
};

void simple_work(Master* master) {
    while (true) {
        sleep(1);
        cout << master->increment() << endl;
    }
}

int main() {

    Master master;
    master.register_work(simple_work);
    master.run();
}

再贴一下我尝试写的Rust代码,但是编译不成功

use std::time::Duration;

struct Master {
    counter: i32,
    work: Option<Box<dyn Fn(&mut Master)>>,
}

impl Master {
    fn new() -> Master {
        Master {
            counter: 0,
            work: None,
        }
    }

    fn increment(&mut self) {
        self.counter += 1;
    }

    fn run(&mut self) {

        let mut w = self.work.as_mut().unwrap();
        w(self); // FIXME 这里始终不对,因为拿work用到了self的引用,不能再把self当参数传进去
    }

    fn register<F>(&mut self, f: F)
    where
        F: Fn(&mut Master) + 'static
    {
        self.work = Some(Box::new(f));
    }
}

fn simple_work (m: &mut Master) {
    loop {
        std::thread::sleep(Duration::from_secs(2));
        m.increment();
        println!("{}", m.counter);
    }
}

fn main() {

    let mut m = Master::new();
    m.register(simple_work);
    m.run();
}

评论区

写评论
Grobycn 2023-04-20 14:33

take 再放回去可行。

但是更改一下程序结构感觉更好,Masterwork 之间本来没有耦合,为什么要按照面向对象的思路将 work 放在 Master 里呢,将它们放在第三方不是更好吗。

use std::time::Duration;

struct Master {
    counter: i32,
}

impl Master {
    fn new() -> Master {
        Master {
            counter: 0,
        }
    }

    fn increment(&mut self) {
        self.counter += 1;
    }
    
    fn register<F: Fn(&mut Self)>(self, f: F) -> Registry<F> {
        Registry {
            master: self,
            work: f,
        }
    }
}

struct Registry<F> {
    master: Master,
    work: F,
}

impl<F: Fn(&mut Master)> Registry<F> {
    fn run(&mut self) {
        let Self { ref mut master, ref mut work } = *self;
        work(master)
    }
}

fn simple_work (m: &mut Master) {
    loop {
        std::thread::sleep(Duration::from_secs(1));
        m.increment();
        println!("{}", m.counter);
    }
}

fn main() {
    let m = Master::new();
    let mut reg = m.register(simple_work);
    reg.run();
}
ThalliMega 2023-04-19 23:52
use std::time::Duration;

struct Master {
    counter: i32,
    work: Option<Box<dyn Fn(&mut i32)>>,
}

impl Master {
    fn new() -> Master {
        Master {
            counter: 0,
            work: None,
        }
    }

    // fn add_counter(&mut self) {
    //     self.counter += 1;
    // }

    fn run(&mut self) {
        let w = self.work.as_mut().unwrap();
        w(&mut self.counter);
    }

    fn register<F>(&mut self, f: F)
    where
        F: Fn(&mut i32) + 'static,
    {
        self.work = Some(Box::new(f));
    }
}

fn self_count(m: &mut i32) {
    loop {
        std::thread::sleep(Duration::from_secs(2));
        *m += 1;
        println!("{}", m);
    }
}

fn main() {
    let mut m = Master::new();
    m.register(self_count);
    m.run();
}
viruscamp 2023-04-19 23:46

你的代码跟 https://zhuanlan.zhihu.com/p/405160594 他的有点像,文章也提供了一些更改的设计。

如果只是想要跑起来,至少可以这样

    fn run(&mut self) // 本来的函数签名写错了吧,<F> 和参数 f 都是多余的
    {
        let mut w = self.work.take(); // 临时断开 Master 和 work
        w.as_ref().unwrap()(self); // 在不消耗 work 的情况下调用
        self.work = w; // 还回去
    }

还有一个方案是把 work 限制成 fn(&mut Master) 虽然不能接受闭包了,但不需要 mut 就可以调用

struct Master {
    counter: i32,
    work: Option<fn(&mut Master)>,
}

    fn run(&mut self)
    {
        self.work.as_ref().unwrap()(self);
    }
YiiSh 2023-04-19 23:36

用 take ,操作完,再放回去。

1 共 4 条评论, 1 页