惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
GbyAI
GbyAI
F
Fortinet All Blogs
Y
Y Combinator Blog
I
InfoQ
Microsoft Azure Blog
Microsoft Azure Blog
C
Cyber Attacks, Cyber Crime and Cyber Security
Vercel News
Vercel News
T
The Blog of Author Tim Ferriss
P
Proofpoint News Feed
S
Schneier on Security
NISL@THU
NISL@THU
The Hacker News
The Hacker News
Simon Willison's Weblog
Simon Willison's Weblog
The GitHub Blog
The GitHub Blog
MyScale Blog
MyScale Blog
Cisco Talos Blog
Cisco Talos Blog
F
Full Disclosure
P
Palo Alto Networks Blog
T
The Exploit Database - CXSecurity.com
Know Your Adversary
Know Your Adversary
A
About on SuperTechFans
The Cloudflare Blog
T
Threat Research - Cisco Blogs
M
MIT News - Artificial intelligence
宝玉的分享
宝玉的分享
雷峰网
雷峰网
Hugging Face - Blog
Hugging Face - Blog
博客园 - 三生石上(FineUI控件)
D
DataBreaches.Net
Martin Fowler
Martin Fowler
H
Help Net Security
B
Blog RSS Feed
B
Blog
爱范儿
爱范儿
V
V2EX
I
Intezer
L
LangChain Blog
WordPress大学
WordPress大学
小众软件
小众软件
美团技术团队
Latest news
Latest news
H
Hackread – Cybersecurity News, Data Breaches, AI and More
T
Tor Project blog
L
Lohrmann on Cybersecurity
Cyberwarzone
Cyberwarzone
Last Week in AI
Last Week in AI
Project Zero
Project Zero
C
Cybersecurity and Infrastructure Security Agency CISA
V
Visual Studio Blog

The Rust Programming Language Forum - Latest topics

Beginner building a Rust backend framework (AI-assisted) — feedback appreciated C++ to Rust -- Exceptions Re-exporting a trait with a custom derive_macro Gold linker is deprecated Why is using PhantomData valid in this case? Code review for Static Pool Allocator Looking for a pool based allocator Why is this legal? What does it mean for Rustc to run "out of TLS keys"? Using async/await internally with no internal runtime, but exposing a nonblocking poll API — is this a reasonable design? Testing functions that use randomness `cargo-path`: improve coding agents&#39; ability to find Rust documentation Rust task runners Lifetime weird case Windows - USB device not detected A random rustc-ice-[...].txt file appeared Develop rust where the environment is setup in a docker Undefined Behavior: in-bounds pointer arithmetic failed: attempting to offset pointer by 20 bytes, but got alloc238 which is only 1 byte from the end of the allocation Unbug 0.5 - Runtime debug assertions Is there a tiny error in section 6.2 Reference types? Lifetime woes implementing ratatui::Widget for a reference Rusqlite + Chrono: How do I simplify code to obtain chrono datetime value From OOP to Rust – struggling with code organization and data structure design Way to avoid a self-referential struct Rust RF and audio resources/communities Whyhttp - HTTP mocks that fail where the bug actually is Using tokio channel permits in a tower service Arc::increment_strong_count design question (cross-post) `&T`, `&mut T`, `Pin<&mut T>` and `&Cell<T>`: Ways of Borrowing a `T` Ratatui detect arrow key press and release Feedback about post in medium Debugger not showing/offering variables? nestrs: a NestJS-style framework for Rust built on Axum and Tower Confusion about struct variance with &mut dyn Narwhal v0.6.0 – message broker for edge apps, now with channel persistence Quiche client sent message not received at the server side "mysql" "Packets out of sync" - workaround Consistency f64 and std::Simd<f64, N> Signal semantics for Rust Snowflake-gen 1.0 - Configurable Snowflake ID generator with thread-local global API How do I do hmr in rust Diving into Rust: My Journey So Far and a Few Hiccups Celerity 0.1.1: pure Rust ZMTP 3.1 with a sans-IO core, Tokio transport, and CURVE-RS How to call struct method as function pointer in loop Loops inside loops and mutable reference on Iterator VSCode - rust-analyzer - annoying re-builds due to RUSTC_BOOTSTRAP changed Borsh v1.6.1 example What's everyone working on this week (17/2026)? Cargo test's verbose output - can I quiet it? Sem: semantic entity level version control CLI built with tree-sitter How do i get all the words in a string and convert it into a vector? Looking for feedback on my first tokenizer in Rust Frostmirror - (lightweight airgap alternative inspired from panamax and romt) - an offline cargo repository for airgap system that can be deployed through a docker image Does "ill-formed, no diagnostic required" exist in Rust? If a program is built with software floating-point emulation, is it affected by hardware floating-point environment? Rustc-link-lib static library How does crates.io and docs.rs store crates? Various input in one line error[E0080]: index out of bounds error when trying to install LuaCAD CSV to SQLite - Is it better to write records as they are being read Looking for feedback for a graph-based task scheduler Confusion: struct directly followed by impl? This is my first project. I invented a new algorithm that even AI don't know. It's about bigint. And I'm 15. Thanks for watching. By the way, my crate named `nna` Why Windows rustc uses a different color scheme? Associate a struct with every enum variant Which Windows env var is responsible for DNS resolition? Clap completion question Is RMW operation less prone to reading stale values than a pure load? Crates with excluded proprietary parts Why are builds of my crate's test not consistent (cargo build vs cargo test vs VSCode-Test-Explorer) Why there is no downcast method Type inference of generic parameters How to make Rust as vertical as possible? Cyw43 on STM32H723 Typing is fun! Another crate for self-referential structs How to use build-analysis feature? Issues with Boxed trait objects Rust-analyzer Language Server Loops with accumulated state confuse me LoMux: Adobe Media Encoder Alternative (First Rust Project) Cbor-core: A deterministic CBOR::Core encoder/decoder Numa - a DNS resolver from scratch How should I deal with copying and renaming in monitoring of files? Request get to api.github.com Compiler Preprocessing Optimization Lifetime trait bounds issue High-Performance API Gateway Implemented a Rust-based CLI tool that supports Ollama's local LLMs Egui_tracing 0.3 — a live tracing/log viewer widget for egui apps Elevation-kit capstone project Panic-free-friendly standard library Work around library with private trait in public trait bound My custom Tab implementation with Slint How to express this complex struct definition which uses closures and multiple layers of higher-ranked lifetimes in Rust? 30K lines of no_std Rust: a bare-metal ARM64 hypervisor that replaces Google's Hafnium SPMC Imperartive or functional? Any improvements? Loop unrolling in manually written loops vs iterators (AVX512) Announcing cachekit 0.7 — pluggable cache policies and arena-backed primitives Need some suggestions Having Multiple Mutable References via Iterators
Why can't I upgrade a known-exclusive &T to &mut T?
jacobsa · 2026-04-16 · via The Rust Programming Language Forum - Latest topics

April 16, 2026, 5:38am 1

Let's say I've written some code that borrows a shared reference to some data, but I happen to be able to prove that it is the only shared reference to that data. Is it okay to play pointer tricks in order to "upgrade" it to an exclusive reference?

The real use case here is writing an iteration algorithm only once, for shared references, and then implementing iter_mut on top of it. Because iter_mut borrows the container exclusively, I know that the shared references returned by iter are the only ones that currently exist.

Here is a simplified example (playground). Is this UB?

struct S<T>(T);

fn upgrade_ref<'a, T>(s: &'a mut S<T>) {
    let t: &T = &s.0; // No problem.
    let p: *const T = t; // No problem.
    let p: *mut T = p as *mut T; // No problem.
    drop(t); // There is no shared reference left (?).
    let t_mut: &'a mut T = unsafe { &mut *p };
}

Well okay, now that I write that I see that the compiler has some data flow analysis that is smart enough to tell me off somehow:

error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
  --> src/lib.rs:10:37
   |
 8 |     let p: *mut T = p as *mut T; // No problem.
   |                     ----------- casting happened here
 9 |     drop(t);  // There is no shared reference left (?).
10 |     let t_mut: &'a mut T = unsafe { &mut *p };
   |                                     ^^^^^^^
   |
   = note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
   = note: `#[deny(invalid_reference_casting)]` on by default

So I guess it's not supposed to be allowed. But then my question is: why? What reasonable optimization is impeded by the assumptions in this function?

And then, assuming it's UB for a good reason, what is the usual pattern for not needing to write an iterator twice for each container? I guess I can write an iterator that produces raw pointers and map those to references of each type; is there a better way?

1 Like

how?

if it's compile time, then you should already have an &mut T. if it's at runtime, then you must bypass the type system and borrow checker, i.e. using raw pointers instead.

it's UB to do anything that's equivalent to transmutation from a shared reference to exclusive reference.

raw pointers must be used, and only if the raw pointer have provanence that's derived from an exclusive borrow.

3 Likes

jacobsa April 16, 2026, 5:56am 3

As in the example: I have an exclusive reference to the containing struct, and no references/pointers are exposed to other code in any other way.

Right, and in this case it seems like that is true. The new exclusive reference to T ultimately comes from &mut S<T>.

EDIT: see next reply below

this paricular example is ok. the raw pointer has the correct provenance, and the access pattern doesn't violates the rules for shared and exclusive references.

note, drop(t) is not needed, and it's useless. the lifetime analysis is based on last use of the reference, not dropping scopes. besides, shared references are Copy, so dropping it is meaningless.

opps, misread the code snippet. the code as-is is wrong, but if you change it to this, then it is ok:

fn upgrade_ref<'a, T>(s: &'a mut S<T>) {
+   let p: *const T = &mut s.0; // No problem.
    let t: &T = &s.0; // No problem.
-   let p: *const T = t; // No problem.
    let p: *mut T = p as *mut T; // No problem.
    drop(t); // There is no shared reference left (?).
    let t_mut: &'a mut T = unsafe { &mut *p };
}
  • Transmuting an & to &mut is always Undefined Behavior.
  • No you can’t do it.
  • No you’re not special.

You can try running your code under MIRI, it will claim you have UB. That's because even if you can prove that a shared reference is unique the compiler will still assume that the pointee cannot be mutated through it.

11 Likes

giocri April 16, 2026, 6:38am 7

there is a way to handle "this is behind a shared reference but i am confident i have exclusive acces to it at this time" and it is unsafecell/refcell depending on wether you want a runtime check of this assumption.

regarding iterators yeah you need two distinct functions for mutable and imutable iteration, it might be a bit annoying but it also lessens your constraints for immutable iteration so you can make it quite a bit more optimized in a lot of cases

One simple counter-example is:

fn bad_ref_use(s: &mut S, new_value: T) {
    let t = &s.t;
    // inventing a read to s.t is fine here because it's not legal to mutate s.t
    // using t and there is no other access to s

    // (instantly) UB transmute
    let t_mut = unsafe { &mut *(t as *const T as *mut T) };
    // not required for UB, but to demonstrate the issue
    *t_mut = new_value;

    // perfectly valid to use invented read here, returning old value
    s.t
}

But as with most optimization this compounds quickly once you start getting multiple procedures etc. involved. This "code motion" optimization - being able to move accesses and computation around in the generated code - is fundamentally important to be able to generate better code, and it depends on being able to quickly classify large branches of the data paths for values as not interacting with each other: exactly what a shared ref provides.

3 Likes

The correct way to do this is to have a &UnsafeCell<T> instead of a &T. You can then convert an &UnsafeCell<T> into a &mut T as long as no other &mut T exists.

1 Like

jacobsa April 16, 2026, 9:27am 10

Thanks @simonbuchan for the example of an optimization that motivates this. I totally believe it's considered "UB in spirit", although I'm not 100% sure I can see why this is UB based on what's listed in [undefined.alias]. I would love to know if someone can be a little more precise about why specifically this is problematic than just "you should feel bad about even thinking of it" vibes.

Thanks, this is probably the real answer in practice—store the values as UnsafeCell<T> internally, have an internal iterator that provides &UnsafeCell<T>, and map its result to &T for iter and &mut T for iter_mut.

FYI, you can convert a &mut T into a &UnsafeCell<T>. UnsafeCell in std::cell - Rust

1 Like

Those docs say:

&T must point to memory that is not mutated while they are live (except for data inside an UnsafeCell<U>),

2 Likes

giocri April 16, 2026, 9:57am 13

this has to be one of the single oddest functionalities i have ever seen i am so curious about the usecases

jacobsa April 16, 2026, 10:04am 14

Right. But in my example the &T isn't live once the memory is mutated, or at least the example can be minimally changed to make that so. Further, the posts above claim that the mere existence of the &mut T is UB, not mutating memory with it.

I think it’s critical to note that pointers mostly remember where they came from (as described in the language semantics, as used in compiler optimizations, and as implemented by interpreters like Miri, even if not at runtime in compiled code). Even if you drop the source & reference, in order to continue to use other pointers or references derived from it, the source & permissions must not be violated. I suppose the source permissions must still be “live” in some sense.

Though, I’m not entirely confident about the granularity of pointer permissions/provenance - I think it’s per-byte. That is, you could have a &u16, get a *const u8 to one of its bytes, drop the &u16, write to the other byte, and read from the *const u8 without UB.

Indeed. You can think of creating a & as giving the compiler permission to read bytes from the referent and creating a &mut as giving the compiler permission to read or write bytes from the referent (up to the length of the referent), as long as it does not change the behavior of your code (unless the code already had UB).

The compiler does actually insert “fake reads” and “fake writes” of & or &mut references in some places, such as the start of functions. Theoretically, it could introduce them in more places (again, so long as it does not change the behavior of a UB-free program).

AFAIK it’s still an open question as to whether the compiler is allowed to assume that the referent of a &T or &mut T is always a valid T, but the opsem team is leaning towards “no”: reads (and possibly writes) of the referent are permitted, but the compiler cannot assume that whatever bytes it reads always form a T. (In other words, if the opsem team affirms this, you could cast a &mut [MaybeUninit<u8>] to &mut [u8], and if whoever uses the &mut [u8] only writes to the slice and never reads from it, it could be sound.)

1 Like

I believe the relevant portion is:

&mut T must point to memory [that is ... and] that no other reference points to while they are live.

which is somewhat atypically terse for the Rust reference.

It's much more explicitly called out by the Rustinomicon:

  • Transmuting an & to &mut is Undefined Behavior. While certain usages may appear safe, note that the Rust optimizer is free to assume that a shared reference won’t change through its lifetime and thus such transmutation will run afoul of those assumptions. So:
    • Transmuting an & to &mut is always Undefined Behavior.
    • No you can’t do it.
    • No you’re not special.

Well, Rust's UB is just the set of things rust defines as UB, if you really want to get pedantic and reductionist about it.

Semantically it's probably best to think of it in the context of "a programming language is a definition of the interface between a programmer and a compiler" - for example the language defines what an "if statement" is in terms of the syntax and semantics: it is something the compiler must implement in order to be called a Rust compiler, and something the programmer can type in a Rust source file to get the described behavior.

In this context, Undefined Behavior is a declaration that a Rust source file does not contain this - if you type statements that would transmute a ref to a mut ref, then you are not meeting the contract required of you by the language, just as much as a compiler failing to produce the semantics of an if statement would be. In other words, the input is no longer Rust source code.

Most of the time you don't need to worry about this in safe languages: the language requires the compiler to reject any input that does not meet the syntactic or semantic input specified (with the strange effect that this is not rust source code is in fact a Rust source file: just an invalid one), but for unsafe the burden is shifted onto you, and you can produce files which a compiler is not required to (and generally won't) reject, but are not valid Rust code.

So, yeah, it's pretty much "you should feel bad", because you're letting down the compiler :stuck_out_tongue:

9 Likes

And the best solution in terms of soundness is still to implement iter_mut separately.

If your iter somehow (due to a bug?) goes twice over the same element, that's safe by itself, but the upgrade UnsafeCell->&mut T will give two mutable references which are UB.
So you need stricter safety justifications on any unsafe in iter_mut; better if there is not any unsafe code at all.

1 Like

mroth April 16, 2026, 2:11pm 18

Maybe it helps to think of the “vibes” this way: &T suggests something like a nice, boring, Rusty pointer to stable data, almost like data in ROM, or at least memory that is not going to change.

So when the compiler sees something like let v = *some_ref, it is free to lean into those vibes and think: “Alright, this is just a read from stable memory. I might do that read later, or do it more often than the source code seems to suggest, especially if I am under the usual constant pressure from my tragically limited CPU registers.”

1 Like

kornel April 16, 2026, 3:52pm 19

It is disallowed on principle, to allow the compiler to make simplified and generalised assumptions about it, that hold all the time everywhere, without taking on complexity of checking these assumptions on case-by-case basis.

There will be code in the compiler that performs some analysis or transformation that is only valid if the data is frozen, and do it blindly and unconditionally on &-without-UnsafeCell.

You're thinking about your code in this specific case and for you this is obvious from what you're looking at. But the compiler is written to compile all code from anywhere written by anyone, and has to be correct for everything from trivial cases to massively tricky jungles of references in huge programs.

Implementing and optimizing for theoretical simplifications is easier and less computationally expensive than compiling with fewer broad guarantees and with need to re-check the actual context to precisely track where something immutable actually is mutable.

4 Likes

Tom47 April 16, 2026, 9:21pm 20

Well, I guess it is actually covered by:

The following list is not exhaustive [...]

Please read the Rustonomicon before writing unsafe code.

a few paragraphs above ...

... since I also think your example is not covered by

&T must point to memory that is not mutated while they are live

nor

&mut T must point to memory [...] that no other reference points to while they are live.

as there are no live references pointing to *p at the point where t_mut is created. (Well, there is s. Is that a problem? And now that I think of it ... how does this latter part not cover reborrows?)