gpt4 book ai didi

rust - 闭包可能比当前函数长寿

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

我刚开始学习 Rust。为此,我正在用 Rust 重写我的 C++ 项目,但最大的问题是闭包的生命周期等。

我为我的问题创建了一个绝对最小的场景 here及以下:

use std::sync::Arc;
use std::cell::{RefCell, Cell};

struct Context {
handler: RefCell<Option<Arc<Handler>>>,
}

impl Context {
pub fn new() -> Arc<Context> {
let context = Arc::new(Context{
handler: RefCell::new(None),
});

let handler = Handler::new(context.clone());

(*context.handler.borrow_mut()) = Some(handler);

context
}

pub fn get_handler(&self) -> Arc<Handler> {
self.handler.borrow().as_ref().unwrap().clone()
}
}

struct Handler {
context: Arc<Context>,

clickables: RefCell<Vec<Arc<Clickable>>>,
}

impl Handler {
pub fn new(context: Arc<Context>) -> Arc<Handler> {
Arc::new(Handler{
context: context,

clickables: RefCell::new(Vec::new()),
})
}

pub fn add_clickable(&self, clickable: Arc<Clickable>) {
self.clickables.borrow_mut().push(clickable);
}

pub fn remove_clickable(&self, clickable: Arc<Clickable>) {
// remove stuff ...
}
}

struct Clickable {
context: Arc<Context>,

callback: RefCell<Option<Box<Fn()>>>,
}

impl Clickable {
pub fn new(context: Arc<Context>) -> Arc<Clickable> {
let clickable = Arc::new(Clickable{
context: context.clone(),

callback: RefCell::new(None),
});

context.get_handler().add_clickable(clickable.clone());

clickable
}

pub fn remove(clickable: Arc<Clickable>) {
clickable.context.get_handler().remove_clickable(clickable);
}

pub fn set_callback(&self, callback: Option<Box<Fn()>>) {
(*self.callback.borrow_mut()) = callback;
}

pub fn click(&self) {
match *self.callback.borrow() {
Some(ref callback) => (callback)(),
None => (),
}
}
}

struct Button {
context: Arc<Context>,

clickable: Arc<Clickable>,
}

impl Button {
pub fn new(context: Arc<Context>) -> Arc<Button> {
let clickable = Clickable::new(context.clone());

let button = Arc::new(Button{
context: context,

clickable: clickable.clone(),
});

let tmp_callback = Box::new(|| {
button.do_stuff();
});
clickable.set_callback(Some(tmp_callback));

button
}

pub fn do_stuff(&self) {
// doing crazy stuff
let mut i = 0;

for j in 0..100 {
i = j*i;
}
}

pub fn click(&self) {
self.clickable.click();
}
}

impl Drop for Button {
fn drop(&mut self) {
Clickable::remove(self.clickable.clone());
}
}

fn main() {
let context = Context::new();

let button = Button::new(context.clone());

button.click();
}

我只是不知道如何在闭包中传递引用。

另一个丑陋的事情是我的 Handler 和我的 Context 需要彼此。有没有更好的方法来创建这种依赖关系?

最佳答案

开始你的初始代码

pub fn new(context: Arc<Context>) -> Arc<Button> {
let clickable = Clickable::new(context.clone());

let button = Arc::new(Button{
context: context,

clickable: clickable.clone(),
});

let tmp_callback = Box::new(|| {
button.do_stuff();
});
clickable.set_callback(Some(tmp_callback));

button
}

首先,让我们记下您遇到的错误

    error[E0373]: closure may outlive the current function, but it borrows `button`, which is owned by the current function
--> src/main.rs:101:37
|
101 | let tmp_callback = Box::new(|| {
| ^^ may outlive borrowed value `button`
102 | button.do_stuff();
| ------ `button` is borrowed here
|
help: to force the closure to take ownership of `button` (and any other referenced variables), use the `move` keyword, as shown:
| let tmp_callback = Box::new(move || {

注意底部的 help block ,您需要使用 move 闭包,因为当 new 函数结束时, button 堆栈上的变量将超出范围。避免这种情况的唯一方法是将其所有权移至回调本身。因此你会改变

let tmp_callback = Box::new(|| {

let tmp_callback = Box::new(move || {

现在,你会得到第二个错误:

    error[E0382]: use of moved value: `button`
--> src/main.rs:107:9
|
102 | let tmp_callback = Box::new(move || {
| ------- value moved (into closure) here
...
107 | button
| ^^^^^^ value used here after move
|
= note: move occurs because `button` has type `std::sync::Arc<Button>`, which does not implement the `Copy` trait

而且这里的错误可能更清楚一点。您试图将 button 值的所有权移至回调闭包中,但您new 函数的主体中使用它你归还它,你不能让两个不同的东西试图拥有这个值(value)。

希望解决方案与您猜想的一样。您必须制作一份您可以拥有的副本。然后你会想要改变

let tmp_callback = Box::new(move || {
button.do_stuff();

let button_clone = button.clone();
let tmp_callback = Box::new(move || {
button_clone.do_stuff();

现在您已经创建了一个新的 Button 对象,并为该对象本身返回了一个 Arc,同时还赋予了第二个 Arc 的所有权> 到回调本身。

更新

鉴于您的评论,这里确实存在循环依赖性问题,因为您的 Clickable 对象拥有对 Button 的引用的所有权,而 Button 拥有对 Clickable 的引用的所有权。解决此问题的最简单方法是第三次更新该代码,从

let button_clone = button.clone();
let tmp_callback = Box::new(move || {
button_clone.do_stuff();

let button_weak = Arc::downgrade(&button);
let tmp_callback = Box::new(move || {
if let Some(button) = button_weak.upgrade() {
button.do_stuff();
}
});

所以 Clickable 将只持有对 Button 的弱引用,如果不再引用 Button,回调将是空操作。

您可能还想考虑将 clickables 设为 Weak 引用列表而不是强引用列表,这样当它们引用的项目是已删除。

关于rust - 闭包可能比当前函数长寿,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45745415/

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