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

如何在Rust中实现侵入式列表?(闭包场景下段错误修复)

用Rust实现Crafting Interpreters中的侵入式开放Upvalue列表

原代码存在几个核心问题导致段错误,逐一拆解并给出正确实现:

核心问题分析

  1. 局部变量指针悬空:allocate_upvalue中把栈上临时value的指针存入ObjUpvalue,函数返回后该临时变量销毁,指针变成野指针,直接触发段错误。
  2. 链表遍历逻辑错误:原C代码是遍历到upvalue->location > local的节点继续前进,Rust代码中条件写反,且用swap修改指针的方式完全错误,没有正确遍历链表。
  3. 不必要的内存包装:closed字段用Box<Value>纯属多余,直接存储Value即可,增加了不必要的内存间接层。
  4. 指针操作不当:修改链表节点的next指针时使用swap是错误的,应该直接赋值更新链表关系。

正确实现方案

首先定义基础结构(根据你的Value类型补充完整):

#[derive(Clone)]
enum Value {
    ValNil,
    ValNumber(f64),
    ValBool(bool),
    // 其他Value类型按需添加
}

struct ObjUpvalue {
    // 开放状态:指向VM栈上的Value;关闭状态:指向自身的closed字段
    location: *mut Value,
    // 关闭后存储的备份值
    closed: Value,
    // 侵入式链表的下一个节点指针
    next: *mut ObjUpvalue,
}

struct VM {
    open_upvalues: *mut ObjUpvalue,
    // VM的栈,存储局部变量
    stack: Vec<Value>,
    // VM其他字段(如全局变量、当前函数等)按需添加
}

1. 分配Upvalue

直接传入栈上Value的有效指针,避免悬空指针:

impl VM {
    fn allocate_upvalue(&mut self, location: *mut Value) -> *mut ObjUpvalue {
        let upvalue = Box::new(ObjUpvalue {
            location,
            closed: Value::ValNil,
            next: std::ptr::null_mut(),
        });
        Box::into_raw(upvalue)
    }

2. 捕获Upvalue(对应C的captureUpvalue)

严格复刻C的链表遍历和插入逻辑:

fn capture_upvalue(&mut self, local: *mut Value) -> *mut ObjUpvalue {
        unsafe {
            let mut prev_upvalue: *mut ObjUpvalue = std::ptr::null_mut();
            let mut upvalue = self.open_upvalues;

            // 遍历链表,找到第一个location <= local的节点(C逻辑:upvalue->location > local时继续)
            while !upvalue.is_null() && (*upvalue).location > local {
                prev_upvalue = upvalue;
                upvalue = (*upvalue).next;
            }

            // 找到已存在的upvalue,直接返回
            if !upvalue.is_null() && (*upvalue).location == local {
                return upvalue;
            }

            // 创建新upvalue并插入链表的正确位置
            let created_upvalue = self.allocate_upvalue(local);
            (*created_upvalue).next = upvalue;

            if prev_upvalue.is_null() {
                self.open_upvalues = created_upvalue;
            } else {
                (*prev_upvalue).next = created_upvalue;
            }

            created_upvalue
        }
    }

3. 关闭Upvalue(对应C的closeUpvalues)

正确处理值的拷贝和指针切换:

fn close_upvalues(&mut self, last: *mut Value) {
        unsafe {
            while !self.open_upvalues.is_null() && (*self.open_upvalues).location >= last {
                let upvalue = self.open_upvalues;
                // 将栈上的值拷贝到closed字段
                (*upvalue).closed = (*(*upvalue).location).clone();
                // 将location指向自身的closed字段,完成关闭
                (*upvalue).location = &mut (*upvalue).closed as *mut Value;
                // 从开放列表中移除当前节点
                self.open_upvalues = (*upvalue).next;
            }
        }
    }
}

关键注意事项

  • 裸指针安全:所有裸指针操作必须在unsafe块中,且要保证指针指向的内存始终有效:
    • 开放状态下,location指向VM栈上的Value,栈元素未被弹出时指针有效;
    • 关闭状态下,location指向ObjUpvalue自身的closed字段,只要ObjUpvalue未被释放,指针就有效。
  • 侵入式链表维护:完全沿用C的逻辑,通过节点自身的next指针串联,不需要额外的链表容器。
  • 值拷贝:关闭Upvalue时必须拷贝栈上的值到closed字段,避免栈元素销毁后数据丢失。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 10:37:03