如何在Rust中用闭包实现类似React useState的状态函数?
问题描述
尝试在Rust中实现类似React useState的函数:接收初始值,返回状态获取函数与状态修改函数,希望用闭包而非面向对象的getter/setter实现。但调用修改函数后,获取函数返回的仍是初始值,不符合预期。
尝试代码
fn use_state<T: Copy>(value: T) -> (impl Fn() -> T, impl FnMut(T)) { let mut val: T = value; let state = move || -> T { val }; let set_state = move |v: T| { val = v; }; (state, set_state) } fn main() { let (counter, mut set_counter) = use_state(0); println!("{:?}", counter()); // 0 set_counter(1); println!("{:?}", counter()); // 0 but I expected 1 }
问题原因
Rust的move闭包会转移变量所有权。你代码中的两个move闭包各自拿走了val的副本(因为T实现了Copy),导致state和set_state操作的是完全独立的两个变量,修改其中一个自然不会影响另一个。
解决方案
使用Rc<RefCell<T>>实现共享的内部可变性,让两个闭包共用同一个状态变量:
use std::cell::RefCell; use std::rc::Rc; fn use_state<T>(value: T) -> (impl Fn() -> T, impl FnMut(T)) where T: Clone, { let val = Rc::new(RefCell::new(value)); let state_val = Rc::clone(&val); let state = move || state_val.borrow().clone(); let set_state = move |v: T| { *val.borrow_mut() = v; }; (state, set_state) } fn main() { let (counter, mut set_counter) = use_state(0); println!("{:?}", counter()); // 0 set_counter(1); println!("{:?}", counter()); // 1,符合预期 }
关键说明
Rc:允许多个闭包共享同一个值的所有权,通过克隆Rc指针增加引用计数,避免所有权转移导致的变量分离。RefCell:提供内部可变性,允许在持有不可变引用的情况下修改内部值,通过运行时检查确保符合Rust的借用规则。Clone约束:因为RefCell::borrow()返回不可变引用,需要克隆获取值的副本;如果T实现了Copy,也可以直接用*state_val.borrow()替代clone()。
无Clone约束的替代方案
如果不想依赖Clone,可以让获取函数返回Ref<T>,但使用时需要手动解引用:
use std::cell::RefCell; use std::rc::Rc; use std::cell::Ref; fn use_state<T>(value: T) -> (impl Fn() -> Ref<T>, impl FnMut(T)) { let val = Rc::new(RefCell::new(value)); let state_val = Rc::clone(&val); let state = move || state_val.borrow(); let set_state = move |v: T| { *val.borrow_mut() = v; }; (state, set_state) } fn main() { let (counter, mut set_counter) = use_state(0); println!("{:?}", *counter()); // 0 set_counter(1); println!("{:?}", *counter()); // 1 }
内容的提问来源于stack exchange,提问作者programandoconro
相关产品推荐
相关产品推荐

