Expand description
§Operations on places
Birds-eye view of this module
Here are all the items defined by this module without any attributes or other distractions:
pub trait PlaceProxy {
type Target: ?Sized;
}
pub unsafe trait CreateHandle<ProxyTiming: Timing>: PlaceProxy {
type Handle: PlaceHandle<Target = Self::Target>;
const ACCESS: AccessKind;
unsafe fn handle_from_raw(this: *const Self) -> Self::Handle;
}
pub trait PlaceHandle: Sized {
type Target: ?Sized;
}
pub unsafe trait ReadPlace: PlaceHandle {
const ACCESS: AccessKind;
const SAFE: bool;
unsafe fn read_place(self) -> Self::Target;
}
pub unsafe trait MovePlace: ReadPlace {}
pub unsafe trait WritePlace: PlaceHandle {
const ACCESS: AccessKind;
const SAFE: bool;
unsafe fn write_place(self, value: Self::Target);
}
pub unsafe trait BorrowPlace<Output>: PlaceHandle {
const ACCESS: AccessKind;
type Timing: Timing;
const SAFE: bool;
unsafe fn borrow(self) -> Output;
}
pub trait Indexable<Idx> {
type Element: ?Sized;
}
pub unsafe trait IndexPlace<Idx, H, PointeeTiming, PointerTiming>:
Indexable<Idx>
where
H: PlaceHandle<Target = Self>,
PointeeTiming: Timing,
PointerTiming: Timing,
{
type ElementHandle: PlaceHandle<Target = Self::Element>;
const POINTEE_ACCESS: AccessKind;
const POINTER_ACCESS: AccessKind;
const SAFE: bool;
fn index(handle: H, idx: Idx) -> Self::ElementHandle;
}
pub unsafe trait DerefPlace<PointeeTiming, PointerTiming>:
PlaceHandle
where
Self::Target: CreateHandle<PointeeTiming>,
PointeeTiming: Timing,
PointerTiming: Timing,
{
const POINTEE_ACCESS: AccessKind;
const POINTER_ACCESS: AccessKind;
const SAFE: bool;
unsafe fn deref_place(
self,
) -> <Self::Target as CreateHandle<PointeeTiming>>::Handle;
}
pub unsafe trait ProjectPlace<S>: PlaceHandle
where
S: Subplace<Source = Self::Target>,
{
type Projected: PlaceHandle<Target = S::Target>;
unsafe fn project_place(self, subplace: S) -> Self::Projected;
}
pub trait PlaceWrapper {
type Inner: ?Sized;
}
pub unsafe trait WrapPlace<S>: PlaceWrapper
where
S: Subplace<Source = Self::Inner>,
{
type Wrapped: Subplace<Source = Self>;
fn wrap(subplace: S) -> Self::Wrapped;
}
pub unsafe trait DropPlace: PlaceHandle {
unsafe fn drop_place(self);
}
pub unsafe trait DropHusk: PlaceProxy {
unsafe fn drop_husk(this: *mut Self);
}
pub unsafe trait ReadMetadata: PlaceHandle {
fn metadata(self) -> Metadata<Self::Target>;
}
pub unsafe trait ReadVariant: PlaceHandle
where
Self::Target: Matchable,
{
unsafe fn read_variant(self) -> &'static str;
}
pub unsafe trait VariantPlace<const VARIANT: &'static str>:
ReadVariant
where
Self::Target: Matchable,
Self::Target: HasVariant<VARIANT>,
{
type ToVariant: PlaceHandle<Target = VariantType<Self::Target, VARIANT>>;
unsafe fn cast(self) -> Self::ToVariant;
}This module provides traits to customize the place operations of Rust.
§Places and Place Expressions
A place in Rust is a particular location in memory. Places are represented by place expressions, which have the following syntax:
$path: paths that refer to local variables (also parameters) and statics,*$place: dereferencing another place expression,$place[$expr]: indexing into another place expression,$place.$ident: accessing a field of another place expression,($place): parenthesized place expressions,$value: an arbitrary expression; it’s value is stored in a temporary place whose lifetime is determined from its context.
Further reading:
- https://nadrieril.github.io/blog/2025/12/06/on-places-and-their-magic.html
- https://www.ralfj.de/blog/2024/08/14/places.html
Place expressions have a direct representation in the form of a
PlaceHandle. A handle points at a place and is responsible for
performing all available place operations on the represented place.
Any valid place expression is converted into a handle by the compiler. We
represent this conversion with the handle! pseudo-macro, explained below.
The information a handle encodes can be broken into three primary categories:
- The type of the place the handle refers to.
- Runtime information needed to access the place; usually, a pointer.
- The capabilities of the pointer the handle was derived from.
For example:
let a: &mut Struct;
let b: MutHandle<'b, Field> = handle!((*a).field);Semantically, we can tell from the type of the handle that this place
expression is derived from a mutable reference, that the place is of type
Field, and that the handle has permission to access the place for the
lifetime 'b.
The definition of MutHandle<'a, T> itself is
simple: It stores a pointer to the place (the field, in the example above).
It also embeds a PhantomData<&'a mut T> to mark the type T as invariant.
To see how a place handle represents capabilities, observe how we turn this expression back into a mutable reference of the field:
let c: &mut Field = <MutHandle<'_, Field> as BorrowPlace<&mut Field>>::borrow(b);If the handle had been derived from a shared reference (a was &Struct
above), the handle type would instead be RefHandle<'_, Field> which does
not implement BorrowPlace<&mut Field>. It does implement
BorrowPlace<&Field>, so the following code compiles.
let a: &mut Struct;
let b: RefHandle<'b, Field> = handle!((*a).field);
let c: &Field = <RefHandle<'_, Field> as BorrowPlace<&Field>>::borrow(b);§Converting place expressions into handles
Any valid place expression is converted into a handle by the compiler. We give this desugaring as a pseudo1-macro definition:
macro_rules! handle {
($path:path) => { LocalHandle::new(&raw {const,mut} $path) };
($place:place[$expr:expr]) => {
<
typeof($place) as IndexPlace<typeof($expr), _>
>::index(handle!($place), $expr)
};
(*$place:place) => { DerefPlace::deref_place(handle!($place)) };
($place:place.$field:member) => {
let subplace
= subplace!(typeof($place), $field, handle!($place));
ProjectPlace::<typeof(subplace)>::project_place(
handle!($place),
subplace,
)
};
(($place:place)) => { handle!($place) };
// value-to-place coercions are turned into temporaries:
($value:expr) => {{
super let value = $value;
handle!(value)
}};
}Links to the types and traits used: LocalHandle, IndexPlace,
DerefPlace, and ProjectPlace.
The need for a subplace! pseudo-macro might seem surprising, but it’s
required to support place wrappers, which we will cover in the next
section. Without place wrappers, we could simply replace the subplace!
invocation with
<field_of!(typeof($place), $field)>::default().
§Place Wrappers
Place wrappers are a special kind of place proxy. They “physically contain”
the place they are proxying for. A good example is MaybeUninit<T>. To
support subplaces of these place wrappers, the WrapPlace trait exists.
It allows forwarding subplaces to the proxy and changing the subplace access
information. With MaybeUninit<T>, this allows accessing any subplace
under the transformation that its type is wrapped in MaybeUninit. So
given &MaybeUninit<Struct>, the field subplace can be borrowed using &
and it has type &MaybeUninit<Field>.
§Place Operation Traits
Aside from the three basic place operations of ReadPlace,
WritePlace, and BorrowPlace, there are also the following other
operations:
MovePlace– moving out of a place (looks the same as a read operation).IndexPlace– using the index operator on places (place[idx]).DerefPlace– dereferencing a pointer that’s in a place (*place).ProjectPlace– accessing a subplace (place.field).DropPlace– dropping the contents of a place (no surface syntax, emitted by the compiler).ReadMetadata– TODO (no surface syntax, emitted by the compiler).ReadVariant– TODO (no surface syntax, emitted by the compiler).VariantPlace– TODO (no surface syntax, emitted by the compiler).
The place operations are desugared in a similar manner to place expressions. In particular, a place operation always operates upon a place expression, which relies on the desugaring detailed in the previous section. Refer to the operation traits for their desugaring.
§Safety
All operation functions are unsafe, since they have raw pointer arguments
that have safety preconditions. The arguments are raw pointers, because the
values they point to need not be in a valid state (they may be partially
moved out or borrowed).
The safety requirements for the operation functions have not been figured out at this point in time. Since we expect several changes to the design, we do not want to commit to writing down good safety documentation before having finished the design.
What is clear at the moment is that the safety requirements will heavily interact with the borrow checker. It will ensure that simultaneous place operations on the same value are allowed, since they either affect disjoint subplaces, or because they both only require shared access. For example:
- reading
ptr.field.subfieldand borrowingptr.fieldwith&Tare allowed to happen at the same time, - writing
ptr.fieldand borrowingptr.field.subfieldat the same time is not allowed.
The safety of using the place operations via the operators will depend on
the value of the SAFE constant in the operation traits. At the moment we
will only permit a literal value of true or false in implementations. It
will dictate if people have to write for example unsafe { &*ptr } or if
*ptr is allowed. It should be set to true when the borrow checker’s
guarantees of either disjoint subplaces or “all concurrent operations are
shared” are enough to calling the operations’ function correctly. If there
are additional requirements, such as “ptr is valid”, then SAFE should be
set to false. For example, &mut T will have SAFE = true in
ReadPlace, but NonNull<T> will set it to false.
§Handle Validity
TODO
we use the non-existent macro fragment specifiers of
placefor place expressions andmemberfor struct field names and tuple indices. Additionally, we require eager expansion of macros, as we use the output of thehandle!macro in the input of thesubplace!macro. ↩
Modules§
Traits§
- Place
Proxy - A type proxying for a place.
- Create
Handle - TODO: Defines creation of handles.
- Place
Handle - A handle to a place.
- Read
Place let _ = place;– Read from a place.- Move
Place let _ = place;– Move out of a place.- Write
Place place = value;– Write to a place.- Borrow
Place &place/@place– Borrow a place asOutput.- Indexable
place[idx]– Enable indexing intoSelf.- Index
Place place[idx]– Index intoSelfvia the handleH.- Deref
Place *place– Dereference the contents of a place.- Project
Place place.field– Project a handle to a subplace.- Place
Wrapper - Wrap a place, exposing some of its subplaces.
- Wrap
Place place.field– Expose a modified subplace of the wrapped place.- Drop
Place - Drop the contents of a place.
- Drop
Husk - Destroy a
PlaceProxywhere its contents have been moved out/dropped. - Read
Metadata - Obtain the metadata of the contents of a place.
- Read
Variant - Obtain the discriminant of the contents of a place.
- Variant
Place - Cast a handle to a place to a subtype.