Skip to content

Add a conversion from &mut T to &mut UnsafeCell<T> #198

Closed
@joseph-gio

Description

@joseph-gio

Proposal

Problem statement

There should be a safe API that converts an &mut T reference into an &mut UnsafeCell<T> reference within a certain scope.

Motivation, use-cases

When dealing with exclusive references, sometimes you may wish to opt in to temporary shared mutable access. If your type implements Copy, you can use Cell::from_mut to convert a mutable reference to a Cell, which allows interior mutability. For non-Copy types, it is often required to use UnsafeCell to allow interior mutability. Converting from &mut T to &mut UnsafeCell<T> is always sound, however there is currently no safe way to do this so the user is required to use std::mem::transmute. Use of this function is potentially error-prone, as a simple mistake might result in the returned reference having an incorrect lifetime.

For an example of a situation where this would be useful, consider this code I wrote for the bevy game engine:

// SAFETY: Converting `&mut T` -> `&UnsafeCell<T>`
// is explicitly allowed in the docs for `UnsafeCell`.
let world: &'w UnsafeCell<World> = unsafe { std::mem::transmute(world) };
Func::combine(
    input,
    // SAFETY: Since these closures are `!Send + !Sync + !'static`, they can never
    // be called in parallel. Since mutable access to `world` only exists within
    // the scope of either closure, we can be sure they will never alias one another.
    |input| self.a.run(input, unsafe { world.deref_mut() }),
    |input| self.b.run(input, unsafe { world.deref_mut() }),
)

Solution sketches

We should add an associated fn to the UnsafeCell type:

pub fn from_mut(value: &mut T) -> &mut UnsafeCell<T> {
    // SAFETY: UnsafeCell is repr(transparent).
    unsafe { &mut *(value as *mut T as *mut Self) }
}

This returns &mut UnsafeCell<T>, unlike Cell::from_mut which returns &Cell. It was a mistake for Cell::from_mut to work this way, since exclusive references are more useful due to the {Unsafe}Cell::get_mut function.

Links and related work

What happens now?

This issue is part of the libs-api team API change proposal process. Once this issue is filed the libs-api team will review open proposals in its weekly meeting. You should receive feedback within a week or two.

Metadata

Metadata

Assignees

No one assigned

    Labels

    ACP-acceptedAPI Change Proposal is accepted (seconded with no objections)T-libs-apiapi-change-proposalA proposal to add or alter unstable APIs in the standard libraries

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions