ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

编译器错误 E0764:常量中使用可变引用

编译器错误 E0764:常量中使用可变引用 编译器错误 E0764常量中使用可变引用【免费下载链接】rustEmpowering everyone to build reliable and efficient software.项目地址: https://gitcode.com/GitHub_Trending/ru/rust导读本文围绕 Rust 编译器rustc错误码E0764在常量/静态项中使用了可变引用mut展开结合当前仓库中该错误的定义文档、诊断实现与测试用例完整讲解错误的触发场景、背后的设计动机防止“全局可变常量”、修复方案以及const fn为何可以安全使用mut的底层原理。读完本文你将能准确诊断此类编译错误并掌握在const、static与const fn之间正确放置mut引用的边界。一、错误速览E0764 是什么E0764 的官方定义位于 compiler/rustc_error_codes/src/error_codes/E0764.md其核心描述为A mutable reference was used in a constant.在常量中使用了可变引用。也就是说在const或static的求值表达式中如果最终值里包含mut可变引用编译器就会报出 E0764。二、最小复现示例文档给出的最简错误示例fn main() { const OH_NO: static mut usize mut 1; // error! }这里把mut 1对临时值1的可变借用赋给常量OH_NO。编译时会得到类似如下的诊断error[E0764]: mutable borrows of temporaries that have their lifetime extended until the end of the program are not allowed -- src/main.rs:2:34 | 2 | const OH_NO: static mut usize mut 1; // error! | ^^^^ this mutable borrow refers to such a temporary | note: temporaries in constants and statics can have their lifetime extended until the end of the program note: to avoid accidentally creating global mutable state, such temporaries must be immutable三、为什么会禁止设计动机文档明确指出这一限制的存在是为了防止在常量/静态项的最终值中出现可变引用。原因很直接如果你有一个mut i32类型的常量就可以通过该引用修改其指向的值从而让常量实质上变成可变的——即“全局可变状态”。常量在 Rust 语义中意味着“编译期可确定、不可变、可内联到使用点”一旦允许mut泄漏进最终值任何代码都能借由此引用改写“常量”破坏这一保证。值得一提的是文档也给出了设计取舍的说明未来或许存在更细粒度的方案只要mut不“泄漏”到最终值就允许使用但现阶段选择了更保守的策略一律禁止。这种保守策略换来的直接收益是规则简单、可预测而const fn内部借用检查器足以防止新的可变引用逃逸到返回值中详见第五节。源码中的诊断实现当前仓库中 E0764 对应MutableBorrowEscaping诊断结构体位于 compiler/rustc_const_eval/src/diagnostics.rs#[derive(Diagnostic)] #[diag(mutable borrows of temporaries that have their lifetime extended until the end of the program are not allowed, code E0764)] #[note( temporaries in constants and statics can have their lifetime extended until the end of the program )] #[note(to avoid accidentally creating global mutable state, such temporaries must be immutable)] #[help( if you really want global mutable state, try replacing the temporary by an interior mutable static or a static mut )] pub(crate) struct MutableBorrowEscaping { #[primary_span] #[label(this mutable borrow refers to such a temporary)] pub span: Span, pub kind: ConstContext, }可见编译器不仅报错还会附带两条note与一条help直接提示修复方向内嵌可变static或static mut这正是我们下一节要讲的解决方案。四、如何修复三条可靠路径路径 1改用不可变引用T如果语义上只需要只读访问直接把mut换成即可const OK: static usize 1; // ok!路径 2把“可变”挪进static/static mut如果确实需要全局可变状态文档与诊断信息共同给出的建议是使用显式的static mut或具有内部可变性的static// 显式的 static mut编译器允许通过它建立 mut static mut BUFFER: i32 42; const fn ptr_to_buffer() - Optionstatic mut i32 { unsafe { Some(mut *std::ptr::addr_of_mut!(BUFFER)) } } const MUT_TO_BUFFER: Optionmut i32 ptr_to_buffer(); // ok!该例取自 tests/ui/consts/const-mut-refs/mut_ref_in_final.rs因为存在显式的static mut声明可变状态有明确归属因此允许常量/静态引用它测试注释明确写道 “Allowed, because there is an explicit static mut.”。同一文件中还有对应的合法示例static mut MUT_ARRAY: mut [u8] mut [42]; static MUTEX: std::sync::Mutexmut [u8] std::sync::Mutex::new(unsafe { mut *MUT_ARRAY }); // ok!见 mut_ref_in_final.rs“Just statics pointing to mutable statics, nothing fundamentally wrong with this.”路径 3改用具有内部可变性的容器如Mutex/UnsafeCell诊断的help提示 “interior mutablestatic”即通过static MutexT、static AtomicU32这类类型把可变性封装在安全接口之后而非让mut直接暴露在常量值中。注意static mut本身在现代 Rust 中被视为不鼓励的用法依赖static_mut_refs等 lint示例代码中均使用了unsafe这是 Rust 当前语义下的必然要求本文仅作技术说明不构成对生产代码的建议。五、const fn中为什么可以用mut文档强调了一个看似矛盾、实则自洽的事实可变引用不能出现在const/static的最终值里但完全可以出现在const fn的函数体内。const fn foo(x: usize) - usize { let mut y 1; let z mut y; *z x; y } fn main() { const FOO: usize foo(10); // ok! }原因有两层作用域封闭const fn内部的局部变量如y生命周期随函数调用结束而结束mut y无法逃逸到函数返回值之外借用检查器保证了const fn不会返回新建的可变引用。返回值是具体值上述foo返回的是usizey的值mut只是计算过程中的临时工具最终值里根本不存在引用自然不构成 E0764。补充说明文档同时提醒“不可以在const/static中调用普通函数”因为普通函数可能有副作用、无法保证编译期求值而const fn可在编译期求值因此能在常量初始化中放心调用。六、更精细的边界临时值 vs. 显式static mutE0764 的判定并非“一切mut进常量都禁止”而是针对“生命周期被延长到程序结束的临时值”。这一点在测试中有非常清晰的刻画禁止const B: *mut i32 mut 4;对临时值4的可变借用见 mut_ref_in_final.rs允许const B2: Optionmut i32 None;没有实际的可变分配见同文件 L19允许const C: *const i32 { let mut x 42; x 3; x };块先把值 move 出去再取引用见同文件 L35-L39。同一思路在 tests/ui/consts/issue-17718-const-bad-values.rs 也有体现const C1: static mut [usize] mut []; //~ ERROR: mutable borrows of temporaries static mut S: i32 3; const C2: static mut i32 unsafe { mut S }; // ok指向显式 static mut另外static与const一视同仁同样受此限制。测试 mut_ref_in_final.rs 里static RAW_MUT_CAST_S、static RAW_MUT_COERCE_S、const RAW_MUT_CAST_C、const RAW_MUT_COERCE_C四者全部报出 “mutable borrows of temporaries” 错误——无论mut被强转为*mut/*const还是藏在结构体字段中只要指向被延长生命周期的临时值都会被拦截。源码侧如何判定该判定的底层实现位于 compiler/rustc_const_eval/src/check_consts/ops.rs/// This op is for mut borrows in the trailing expression of a constant /// which uses the enclosing scopes rule to leak its locals into anonymous /// static or const items. pub(crate) struct EscapingMutBorrow;EscapingMutBorrow实现了NonConstOptrait其status_in_item直接返回Status::Forbidden恒禁止并在build_error中创建MutableBorrowEscaping即 E0764诊断。这里的 “enclosing scopes rule” 指的正是常量的尾表达式会把局部临时值“泄漏”成匿名静态/常量项使其生命周期延长至程序结束——这正是 E0764 要堵住的洞口。七、常见疑问速查场景是否触发 E0764说明const X: mut i32 mut 1;是可变借用泄漏进最终值static X: mut i32 mut 1;是static同样受限const fn f() { let z mut y; ... }否借用不逃逸、最终值不含引用const X: i32 1;否不可变引用允许static mut S: i32 1; const X: mut i32 unsafe { mut S };否显式static mut提供归属const X: Optionmut i32 None;否最终值不含实际可变分配普通函数调用出现在const初始化中否但报其他错常量中不能调用非常量函数八、小结E0764 是 Rust 为保证常量语义纯净性而设的“防火墙”const/static的最终值中不允许出现mut。遇到该错误时按以下顺序排查确认是否真的需要可变性——若只需只读改用T若需要全局可变状态改用显式static mut或Mutex/AtomicU32等内部可变类型若只是计算过程中的临时借用把逻辑封装进const fn让借用检查器替你兜底。结合 E0764 定义文档、诊断实现、判定逻辑 以及 约束测试用例你可以完整追溯该错误的“为什么”与“怎么办”并把它推广到任何涉及const/static与引用交织的代码审查中。【免费下载链接】rustEmpowering everyone to build reliable and efficient software.项目地址: https://gitcode.com/GitHub_Trending/ru/rust创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表