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

Rust E0507错误:无法在Fn闭包中移动捕获变量的解决方案咨询

Rust闭包所有权错误:无法在Fn闭包中移出捕获变量

问题场景

你封装了with_connection函数管理SQLite连接的生命周期,同时实现了select_one辅助函数,通过传入FnOnce闭包将数据库查询结果行映射为自定义对象,但编译时触发所有权错误。

原始代码

use rusqlite::{named_params, Connection, OptionalExtension, Row, ToSql};
use uuid::Uuid;

#[derive(Debug)]
struct User {
    id: Uuid,
    name: String,
}

fn select_one<T>(
    query: &str,
    params: &[(&str, &dyn ToSql)],
    mapper: impl FnOnce(&Row) -> rusqlite::Result<T>,
) -> rusqlite::Result<Option<T>> {
    with_connection(|connection| {
        let mut statement = connection.prepare(query)?;
        statement.query_row(params, mapper).optional()
    })
}

fn with_connection<T>(action: impl Fn(&Connection) -> T) -> T {
    let connection = Connection::open_in_memory().unwrap();
    let result = action(&connection);
    connection.close().unwrap();
    result
}

fn main() {
    let query = "select id, name from user;";
    let params = named_params! {};
    let user = select_one(query, params, |row| {
        Ok(User { id: row.get(0)?, name: row.get(1)? })
    });
    dbg!(user);
}

编译错误

Compiling playground v0.0.1 (/playground)
error[E0507]: cannot move out of `mapper`, a captured variable in an `Fn` closure
  --> src/main.rs:17:37
   |
13 |     mapper: impl FnOnce(&Row) -> rusqlite::Result<T>,
   |     ------ captured outer variable
14 | ) -> rusqlite::Result<Option<T>> {
15 |     with_connection(|connection| {
   |                     ------------ captured by this `Fn` closure
16 |         let mut statement = connection.prepare(query)?;
17 |         statement.query_row(params, mapper).optional()
   |                                     ^^^^^^ move occurs because `mapper` has type `impl FnOnce(&Row) -> rusqlite::Result<T>`, which does not implement the `Copy` trait

For more information about this error, try `rustc --explain E0507`.
error: could not compile `playground` (bin "playground") due to 1 previous error

错误原因

with_connection的参数被约束为impl Fn(&Connection) -> T,这意味着传入的闭包可以被多次调用。但你捕获的mapper是FnOnce类型——这类闭包只能被执行一次,执行时会消耗自身(所有权转移)。编译器不允许在支持多次调用的Fn闭包里移动FnOnce变量,因为如果闭包被重复调用,mapper已经被移走,会导致非法访问。

解决方法

最直接且合理的修复是修改with_connection的闭包约束为FnOnce,因为实际场景中这个闭包只会被调用一次(打开连接→执行操作→关闭连接,流程只走一遍):

fn with_connection<T>(action: impl FnOnce(&Connection) -> T) -> T {
    let connection = Connection::open_in_memory().unwrap();
    let result = action(&connection);
    connection.close().unwrap();
    result
}

修改后,select_one中捕获mapper的闭包会被标记为只能执行一次,此时移动mapper到query_row中就符合所有权规则了——毕竟你原本就只打算用一次mapper。

内容的提问来源于stack exchange,提问作者Jon

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 05:34:52