You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 10:02:26