External Publication
Visit Post

A read only no alias reference

Rust Internals [Unofficial] June 24, 2026
Source

Pre-RFC: Exclusive shared references for read-only noalias borrows

Summary

Rust currently has two main reference modes:

&T // shared, read-only-ish

&mut T // exclusive, mutable

This post proposes exploring a third reference mode:

&^T // exclusive, read-only

The syntax is only a placeholder, names like &uniq T, &noalias T, or something else may be better.

The basic idea is simple: this would be a reference that is read-only like &T, but exclusive/non-overlapping like &mut T.

If rustc cannot prove that the borrow is exclusive and non-overlapping, creating the borrow should be rejected.

reasons

Rust already has strong aliasing information, but today exclusivity and mutability are mostly bundled together in &mut T.

Sometimes a function does not want to mutate a value, but it still wants to know that the value does not alias anything else. In those cases, using &mut T works mechanically, but it communicates the wrong thing at the API level.

For example:

fn compute(input: &^[f32], output: &mut [f32]) {

*// input is read-only*

*// output is mutable*

*// input and output are guaranteed non-overlapping*

}

The intent is:

  • input will only be read;
  • input does not overlap with output;
  • the compiler can optimize based on that exclusivity.

This is somewhat similar to C’s const T * restrict, except checked by Rust’s borrow checker instead of being trusted as an unsafe programmer promise.

Proposed semantics

For a live &^T borrow:

  1. The referenced bytes must be live, initialized, aligned, non-null, and valid for T.
  2. The borrow may read from T.
  3. The borrow may not write to T.
  4. No other live reference may overlap the pointed-to bytes.
  5. No non-derived raw pointer may read or write the pointed-to bytes while the borrow is live.
  6. Pointers or references derived from the &^T may only read.
  7. &^T is not Copy, because copying it would create two exclusive aliases.
  8. &^T may be reborrowed as &T for a shorter lifetime, but the original &^T is suspended while that reborrow is live.
  9. For DSTs and slices, the exclusivity applies to the whole dynamic byte span.
  10. If rustc cannot prove the borrow is exclusive/non-overlapping, the program is rejected.

The intended distinction is:

&T // shared + read-only-ish

&mut T // exclusive + mutable

&^T // exclusive + read-only

Interaction with

UnsafeCell

For the strongest optimization value, &^T should probably require that T contains no UnsafeCell, or use some kind of Freeze-like property.

Without this restriction, &^T would be read-only through the reference, but the underlying memory could still be mutated through interior mutability. That would make the optimization guarantees much weaker.

So the strongest useful version is something like:

&^T where T: FreezeLike

Meaning: no interior mutable bytes are reachable through this reference.

Potential LLVM lowering

An exclusive shared reference could let rustc safely give LLVM stronger information than it can give for a normal shared reference.

At function boundaries, where valid, &^T could potentially lower to pointer arguments with attributes like:

ptr noalias readonly nonnull noundef align N dereferenceable(M)

Possible LLVM attributes or metadata enabled by this reference kind:

  • noalias: the referenced memory does not alias other active accesses.
  • readonly: the function does not write through this pointer argument.
  • nonnull: references are non-null.
  • noundef: references must be valid values.
  • align N: reference alignment is known.
  • dereferenceable(M): the pointed-to span is known to be dereferenceable.
  • captures(none): when rustc can prove the pointer does not escape.
  • captures(address, read_provenance): when only limited read-only provenance can escape.
  • !alias.scope / !noalias metadata: for scoped noalias information inside function bodies, reborrows, loops, and disjoint slice/range borrows.
  • potentially memory(argmem: read) for functions or calls whose memory effects are limited to reading argument memory.

For example:

fn sum(a: &^[u64], b: &^[u64]) -> u64 {

*// ...*

}

This could tell LLVM that:

  • a and b are readable;
  • neither a nor b is written through;
  • a and b do not overlap;
  • both spans are dereferenceable for their slice lengths.

That could help with vectorization, load hoisting, redundant-load elimination, and alias analysis.

Why not just use

& mut T

?

&mut T already provides exclusivity, but it also means mutable access at the API level.

This proposal is about separating exclusivity from mutability:

exclusive + mutable => &mut T

exclusive + read-only => &^T

shared + read-only => &T

Using &mut T for read-only noalias access works in some cases, but it makes APIs less clear. A function that only reads from a value should not need to request mutable access just to express that the value is unique.

The goal is to allow APIs to say exactly what they mean: this memory is read-only, but it is also exclusive and non-overlapping.

Discussion in the ATmosphere

Loading comments...