Idea: Alias as a common abstraction over Share and Reborrow

I have been thinking about the relationship between the proposed Reborrow work and the recent Share/cheap-clone ideas.

I think there may be a useful common abstraction above them:

unsafe auto trait Alias {}

trait Share: Alias {}
trait Reborrow: Alias {}

The basic idea is that Alias means:

Another handle to the same logical resource can be created without transferring ownership of that resource away from the original value.

Share and Reborrow are then two different aliasing disciplines.

For a shared alias:

Arc<T>: Share

creating another alias simply produces another independent handle to the same underlying resource, and the original remains fully usable.

For a reborrowed alias:

&mut T: Reborrow

creating another alias produces a temporary view of the same resource, but the original is logically restricted for the duration of the alias, according to the usual borrowing rules.

So conceptually:

             Alias
            /     \
        Share     Reborrow

Consider:

struct X<'a, T, C> {
    a: Option<&'a mut T>, // Reborrow -> Alias
    b: Arc<C>,            // Share    -> Alias
}

Here, a participates in reborrowing, while b participates in sharing.

A temporary alias of X is not something you manually construct by rewriting fields. Instead, it is simply a derived view of X that the compiler can form when needed, where:

  • a is reborrowed automatically (if accessed mutably)
  • b is shared (cheaply cloned or reference-counted)

So conceptually, the compiler treats aliasing X as producing a new view:

X (aliased view)
{
    a: reborrowed view of x.a,
    b: shared view of x.b,
}

Copy fields and metadata

I do not think ordinary Copy fields should automatically imply Alias.

Consider:

struct X<'a, T> {
    a: &'a mut T,
    has_been_modified: bool,
}

If aliasing X simply copies the bool, then two aliases could diverge:

  • one alias mutates a and sets has_been_modified = true
  • another alias still sees false

Now the two handles disagree about state that is meant to describe the same logical resource.

The compiler cannot know whether a Copy field is:

  • harmless metadata (len, capacity, etc.)
  • or logically significant state tied to the resource

So the safe default must be conservative:

struct automatically implements Alias
iff every field implements Alias

Copy alone is not sufficient evidence of alias-safety.


Safe opt-in for metadata-heavy types

Types that intentionally include duplicated metadata can opt in manually:

struct SliceMut<'a, T> {
    a: &'a mut T, // Reborrow, therefore Alias
    len: usize,   // Copy, therefore !Alias
}

unsafe impl<T> Alias for SliceMut<'_, T> {}

Here, len is just descriptive metadata, so copying it as part of an alias is fine. The unsafe impl is the author's promise that doing so cannot break the type's invariants.

But not every handle-like type should be able to opt in this way.

For example, MutexGuard<'_, T> carries the responsibility of unlocking the mutex when it is dropped. Creating another guard would duplicate that responsibility, so it should not be Alias.

Roughly:

SliceMut<'_, T>   -> explicit Alias is reasonable
Arc<Mutex<T>>     -> Share -> Alias
&mut T            -> Reborrow -> Alias
MutexGuard<'_, T> -> !Alias

So Alias is not simply "this type refers to something". It means another alias can be created without breaking the rules or responsibilities of the original value.


How this would look in practice

Shared case (Share)

fn use_shared<T: Share>(x: T) {
    foo(x);  // x.share()
    boo(x);  // x.share()
}

Both aliases are independent and fully usable.


Reborrow case (Reborrow with auto-reborrowing)

fn use_reborrow<'a, T>(x: Option<&'a mut T>) {
    foo(x); // compiler may implicitly reborrow
    boo(x); // reborrow rules ensure safety
}

Here, the user does not manually construct aliases. The compiler inserts reborrows as needed, and ensures that all derived uses respect exclusivity.


Mixed struct case (Alias composition)

struct X<'a, T, C> {
    a: Option<&'a mut T>,
    b: Arc<C>,
}
fn process<'a, T, C>(x: X<'a, T, C>) {
    foo(x);
    boo(x);
}

Conceptually:

  • a is reborrowed automatically when needed
  • b is shared via Arc

The important point is that the whole struct can be treated as an Alias, even though its fields use different aliasing disciplines.


Mutex example

fn use_mutex<T>(m: Arc<Mutex<T>>) {
    foo(m); // Share
    boo(m); // Share
}

Here Arc<Mutex<T>> is Share, hence Alias.

But:

fn use_guard<T>(g: MutexGuard<'_, T>) {
    foo(g);
    boo(g); // invalid: would duplicate unlock responsibility
}

MutexGuard is not Alias, because aliasing it would duplicate ownership of the unlock action.

However, the inner &mut T remains a valid Reborrow.


Closing thought

I am curious whether this has already been explored as an explicit trait relationship.

In particular, could Alias be useful as a compiler-known auto trait that:

  • composes structurally across Share and Reborrow
  • is automatically derived when all fields are Alias
  • requires explicit unsafe opt-in when a type includes duplicated metadata that is not itself Alias

It feels like this might be a unifying abstraction over several existing and proposed ownership mechanisms.

I forgot to emphasize one point: this feels like what Alias should mean in Rust, the safe ability to create another alias to the same resource, not necessarily the cheap(est) ability to do so.

One clarification from thinking through counterexamples: &T does not need any special aliasing behavior.

&T is already Copy, so Rust already knows how to duplicate it cheaply. Nothing extra like Alias, Reborrow or Share is needed.

For example:

struct Y<'a, T> {
    a: &'a T,
    len: usize,
}

or even:

struct Y<'a, T> {
    a: &'a RefCell<T>,
    len: usize,
}

can just be Copy (if they derive Clone and Copy). The borrowing rules of RefCell are handled at runtime, so this doesn’t change anything.

The real interesting case is &mut T:

struct SliceMut<'a, T> {
    a: &'a mut T,
    len: usize,
}

This cannot be Copy, but we still want to be able to reborrow the &mut T (like Rust does for &mut [T]) while keeping extra metadata like len.

So the key distinction is:

& T      -> Copy (just duplicate the reference)
&mut T   -> Reborrow (temporarily split access, not copyable)

In short: for references, &T is already handled by Copy; the interesting Alias case is &mut T, where another view requires reborrowing rather than copying.

Use
├── Move
│     transfer the value
│
├── Copy
│     duplicate the value
│
├── Alias
│   ├── Share
│   │     create another independently usable handle
│   │
│   └── Reborrow
│         create a temporary handle;
│         restrict the source while it exists
│
└── Clone (not really important here because it's explicit)
      create another value

Maybe this is closer to how it needs to look:

trait Share: Clone { .. }
trait Reborrow { .. }

unsafe auto trait Alias {}

impl<'a, T: ?Sized> Reborrow for &'a mut T { .. }

impl<T: Copy> !Alias for T {}
impl<T: Share (+ !Reborrow)> Alias for T {}
impl<T: Reborrow (+ !Share)> Alias for T {}

The idea is:

Copy     -> !Alias
Clone    -> says nothing about Alias
Share    -> Alias
Reborrow -> Alias

----------------------------------

&T           -> Copy     -> !Alias
&[T]         -> Copy     -> !Alias

&mut T       -> Reborrow -> Alias
&mut [T]     -> Reborrow -> Alias

*const T     -> Copy     -> !Alias
*mut T       -> Copy     -> !Alias
*const [T]   -> Copy     -> !Alias
*mut [T]     -> Copy     -> !Alias

----------------------------------

T: Copy + Share             -> not allowed
T: Copy + Reborrow          -> not allowed
T: Copy + Share + Reborrow. -> not allowed
T: Share + Reborrow         -> most likely also not allowed

For example:

struct X<'a, T> {
    data: &'a mut T, // Reborrow -> Alias
    name: String,    // Clone only
}

String is not Alias, so X would not automatically become Alias.

Likewise:

struct X<'a, T> {
    data: &'a mut T,
    description: Cow<'a, str>,
}

Cow is Clone, but cloning it can either preserve a borrow or create independent owned data. So Clone clearly cannot be enough to establish Alias.

A custom mutable reference is the opposite example:

struct MyMut<'a, T> {
    ptr: NonNull<T>,
    marker: PhantomData<&'a mut T>,
}

Its fields are mostly Copy, so it would not become Alias structurally. Instead, implementing Reborrow explicitly gives it the intended aliasing semantics.

This feels like a useful boundary: Alias should come from actual Share/Reborrow semantics, not merely from being Copy or Clone.

TODO: Drop scenarios