宿主拿到Wasm内存指针后,把它存进结构体以便下次复用,看起来省了一次查询。但只要中间还可能调用guest,就必须重新判断这份视图是否有效,不能只看Rust结构体还活着。
Wasmtime Memory安全文档明确提醒,普通内存增长可能搬迁底层存储,之前构造的原始指针或切片可能失效。2026-10-23读到的页面标记51.0.0-dev;它不是本机已经编译验证的50.0.0接口。
危险的不只是直接写出 grow
把Store交给另一段代码,或者调用guest导出函数,也可能间接增长内存。安全借用API用Store借用约束阻止某些重入;绕到data_ptr后,维护有效期、别名和并发的责任转回调用者。
这里讨论普通非共享线性内存。共享内存有不同的增长和并发规则;官方文档说明其底层地址不会因增长搬迁,但当前长度仍会改变,数据竞争问题也不会消失。不能把一种情况的图解机械推广到全部Memory。
先练习“短借用,或复制出去”
下面用Vec演示一种访问习惯:范围计算防溢出;借用期间不修改容器;需要跨越可能重分配的操作时先拿拥有所有权的副本。这没有执行Wasm,也没有故意解引用失效指针。
fn range(offset: usize,len: usize,total: usize) -> Option<std::ops::Range<usize>> { let end=offset.checked_add(len)?; (end<=total).then_some(offset..end) } fn main() { let mut memory=vec![1u8,2,3,4]; let owned_copy={ let r=range(1,2,memory.len()).unwrap(); memory[r].to_vec() }; memory.resize(100_000,0); // may reallocate; no borrowed view is retained assert_eq!(owned_copy,vec![2,3]); assert_eq!(&memory[range(1,2,memory.len()).unwrap()],&[2,3]); assert!(range(usize::MAX,2,memory.len()).is_none()); assert!(range(99_999,2,memory.len()).is_none()); println!("checked access and independent copy passed; not a Wasm runtime test"); }rustc 1.98.0运行通过。resize不保证这次真的搬迁,所以实验没有声称观察到地址变化;它只验证安全访问方式和越界拒绝。独立副本仍有效,是因为它拥有另一份数据。
省复制之前先写清访问协议
小结果可以用read/write一类有边界检查的访问方式复制到宿主。若确需临时切片,缩小作用域,结束借用后再调用可能改动Store的函数;再次访问时重新获取范围和视图。offset+len用checked_add,先确认在当前内存长度内。
真正的零拷贝协议还需规定谁能写、何时释放、跨线程如何同步、取消后谁结束访问。本篇不提供绕开借用检查的技巧,也没有验证共享内存、多内存或组件ABI。
结合10月20日发布的Wasmtime 50.0.0做集成时,应固定该版再核接口和启用特性。仅引用最新网页,并不能说明旧代码已适配新运行时。