Skip to content

Implicit Operation Layer

$ enters the implicit operation layer for a value or a type. It exposes low-level operations as explicit method-like calls: taking references, taking raw pointers, explicit dereference, explicit move, freeing resources, querying layout, and casting.

Value operations are written as value$.op(...):

Int value = 42;
Int& ref = value$.ref();
Int* ptr = unsafe {
value$.ptr()
};
Int copied = ref$.get();

Type operations are written as Type$.op(...):

Int size = Int$.size();
Int align = Int$.align();
Int*! value = unsafe { Int$.alloc() };

$ binds to the complete expression on its left:

a$.b // equivalent to (a$).b
a.b$ // equivalent to (a.b)$

Use parentheses for compound expressions:

Int raw = (left + right)$.as(Int);

Do not read $ as part of field access. It first switches the left-hand expression into the implicit operation layer, then resolves the following operation.

OperationMeaning
value$.ref()returns a shared readonly T& borrow
value$.mut_ref()returns a unique mutable T&!; requires a writable place
value$.ptr()returns a readonly T*; requires unsafe
value$.mut_ptr()returns a writable T*!; requires a writable place and unsafe
value$.get()explicitly reads through T^, T&, T&!, T*, or T*!
value$.set(new_value)explicitly writes through a T&! or T*! target
value$.move()forces an explicit move and invalidates the source
value$.addr()gets a raw pointer to the current value; requires unsafe
value$.dealloc()frees an object allocated by the default heap allocator; requires unsafe
value$.as(Type)low-level cast; requires unsafe for raw pointer casts

These operations are not ordinary methods. They are built-in operations for references, ownership, raw pointers, allocation, and low-level casts.

T^ can auto-dereference when the expected type is its pointee type. T& and T* require explicit reads; use $.get() when you want to state the pointee read directly:

Int^ owner = new Int(42);
Int copied = owner$.get();
Int value = 10;
Int& ref = value$.ref();
Int copied_ref = ref$.get();

Member access is the exception: owner.member can pass through T^ to access the owned value.

Non-copyable movable values move by default in value positions. $.move() is the explicit form, and can also force a move instead of the default copy for a Copyable value:

Int^ a = new Int(42);
Int^ b = a$.move();
Int copyable = 1;
Int forced = copyable$.move();

The source is invalid after the move:

Int value = a$.get(); // error: a has been moved.

See Ownership, Borrowing & Lifetimes for the complete ownership rules.

Types can enter the implicit operation layer too:

Int size = Int$.size();
Int align = Int$.align();
Int max_align = Int$.max_align();
Int*! value = unsafe { Int$.alloc() };
Int*! values = unsafe { Int$.alloc(10) };

These operations query layout information or perform low-level allocation. Layout information is also consumed by the backend.

OperationMeaning
Type$.size()type size
Type$.align()ABI alignment
Type$.max_align()maximum Jiang type alignment supported by the default allocator
Type$.alloc()allocates one uninitialized object and returns Type*!
Type$.alloc(n)allocates storage for n uninitialized objects and returns Type*!

Prefer target-type initialization for normal safe conversions. Do not write ordinary conversions as $.as(...):

Float f = Float(10);
Int i = Int(f);

value$.as(Type) only represents a low-level cast: it is closer to interpreting or converting the current value as the target type. Use it mainly for raw pointers, integer addresses, and FFI. Do not use it as the normal spelling for numeric conversions, construction, or everyday APIs.

Int address = 0x1000;
Int* ptr = unsafe {
address$.as(Int*)
};

Low-level memory operations are gated by the unsafe effect. Use unsafe { ... } when a small expression or block needs to take raw pointers, cast raw pointers, allocate raw storage, or explicitly deallocate storage. $.get() and $.set() are not themselves unsafe; the unsafe boundary is the operation that creates or casts the raw pointer.

maybe$.some() force-unwraps an optional value:

Int? maybe = 42;
Int value = maybe$.some();
OperationMeaning
optional$.some()force-unwraps an optional and returns its T value

Prefer .some(...) patterns, guard, or ?? in ordinary code:

Int value = maybe ?? 0;

Forced unwrap is best reserved for low-level paths where surrounding logic already guarantees a present value.