有没有一种方法可以修改闭包外部的字符串而不让该闭包取得它的所有权?

问题描述 投票:0回答:1

我正在尝试编写一个简单的计算器应用程序来掌握 FLTK,特别是 Rust 绑定。对于输出,我想在可以设置输出的地方存储一个字符串,这样处理起来更简单,但是,编译器确实不喜欢这样。

我正在尝试在循环中使用回调,因为我需要为所有按钮执行大约 9-10 次,并且我希望能够对需要一些标准回调的较大按钮数量重复它。

我已经尝试执行编译器的建议,让闭包通过

move
取得所有权,但问题是我无法将字符串取回以供以后使用,因为库使该函数无法拥有它除此之外,将所有权移交给无法将其交还的匿名函数是没有意义的。

我的编译器中当前代码的片段是

fn main() {

    let mut number_buts: [Button; 10] = arr![Button::default(); 10];
  
    let mut out_string: String = String::new();
    for i in 1..number_buts.len()
    {
        let mut current_but = Button::default();
 
        let call_back = |but: &mut Button| {
            out_string.push_str(but.label().clone().as_str());
        };

        current_but.set_callback( call_back);        
        let _ = mem::replace(&mut number_buts[i], current_but);
    }
}

我得到的错误是

error[E0373]: closure may outlive the current function, but it borrows `out_string`, which is owned by the current function
  --> src\main.rs:46:25
   |
46 |         let call_back = |but: &mut Button| {
   |                         ^^^^^^^^^^^^^^^^^^ may outlive borrowed value `out_string`
47 |             out_string.push_str(but.label().clone().as_str());
   |             ---------- `out_string` is borrowed here
   |
note: function requires argument type to outlive `'static`
  --> src\main.rs:50:9
   |
50 |         current_but.set_callback( call_back);
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: to force the closure to take ownership of `out_string` (and any other referenced variables), use the `move` keyword
   |
46 |         let call_back = move |but: &mut Button| {
   |                         ++++

error[E0499]: cannot borrow `out_string` as mutable more than once at a time
  --> src\main.rs:46:25
   |
46 |         let call_back = |but: &mut Button| {
   |                         ^^^^^^^^^^^^^^^^^^ `out_string` was mutably borrowed here in the previous iteration of the loop
47 |             out_string.push_str(but.label().clone().as_str());
   |             ---------- borrows occur due to use of `out_string` in closure
...
50 |         current_but.set_callback( call_back);
   |         ------------------------------------ argument requires that `out_string` is borrowed for `'static`
rust ownership fltk ownership-semantics
1个回答
0
投票

在 Rust 中,闭包通过三种方式捕获其环境:获取所有权、可变借用和不可变借用。当您希望闭包在不获取所有权的情况下修改某些内容时,您将面临 Rust 所有权和借用规则的挑战,特别是在像您这样的场景中,编译器需要确保跨潜在异步或基于回调的代码的内存安全。

考虑到您的要求和遇到的错误,一种解决方案是使用 Rc 来共享所有权和可变借用。 Rc(引用计数)允许相同数据的多个所有者,而 RefCell 提供内部可变性,这意味着即使存在对 RefCell 的不可变引用,您也可以更改数据。

以下是修改代码片段的方法:

use fltk::{prelude::*, *};
use std::cell::RefCell;
use std::rc::Rc;

fn main() {
    let app = app::App::default();
    let mut win = window::Window::default().with_size(400, 300);

    let out_string = Rc::new(RefCell::new(String::new()));
    let mut number_buts: Vec<button::Button> = Vec::new();

    for i in 0..10 {
        let out_str_clone = out_string.clone();
        let mut btn = button::Button::new(40 * i, 0, 40, 40, &i.to_string());
        btn.set_callback(move |_| {
            out_str_clone.borrow_mut().push_str(&i.to_string());
            println!("{}", out_str_clone.borrow());
        });
        number_buts.push(btn);
    }

    win.end();
    win.show();
    app.run().unwrap();
}

主要变更及说明:

  • Rc的使用:这种组合允许多个回调共享所有权并安全地修改字符串。 Rc 允许多个回调共享字符串,RefCell 允许对字符串进行可变访问,即使它是共享的。
  • 克隆 Rc:在进入闭包之前,我们克隆 Rc,这会增加引用计数。这允许每个闭包都有自己的 Rc 指向同一个 RefCell。实际的 String 数据没有被克隆,只是 Rc 指针。
  • 可变借用:在闭包内,我们使用borrow_mut()来获取对String的可变引用并修改它。 RefCell 在运行时检查是否只有一个可变借用。

这种方法非常适合 GUI 等单线程应用程序。但是,如果您在多线程上下文中工作,则 Rc 将不是线程安全的。对于多线程场景,请考虑使用 Arc 或 Arc 来实现线程安全的共享所有权和可变性。

© www.soinside.com 2019 - 2024. All rights reserved.