API Reference

Ownership Macros

BorrowChecker.MacrosModule.@ownMacro
@own [:mut] x = value
@own [:mut] x, y, z = (value1, value2, value3)
@own [:mut] for var in iter
    # body
end
@own [:mut] x  # equivalent to @own [:mut] x = x
@own [:mut] (x, y)  # equivalent to @own [:mut] (x, y) = (x, y)

Create a new owned variable. If :mut is specified, the value will be mutable. Otherwise, the value will be immutable.

You may also use @own in a for loop to create an owned value for each iteration.

source
BorrowChecker.MacrosModule.@moveMacro
@move [:mut] new = old

Transfer ownership from one variable to another, invalidating the old variable. If :mut is specified, the destination will be mutable. Otherwise, the destination will be immutable. For isbits types, this will automatically use @clone instead.

source
BorrowChecker.MacrosModule.@cloneMacro
@clone [:mut] new = old

Create a deep copy of a value, without moving the source. If :mut is specified, the destination will be mutable. Otherwise, the destination will be immutable.

source
BorrowChecker.MacrosModule.@take!Macro
@take! var

Take ownership of a value, typically used in function arguments. Returns the inner value and marks the original as moved. For isbits types, this will return a copy and not mark the original as moved.

source

References and Lifetimes

BorrowChecker.MacrosModule.@lifetimeMacro
@lifetime a begin
    @ref ~a rx = x
    # use refs here
end

Create a lifetime scope for references. References created with this lifetime are only valid within the block and are automatically cleaned up when the block exits.

source
BorrowChecker.MacrosModule.@refMacro
@ref ~lifetime [:mut] var = value
@ref ~lifetime [:mut] (var1, var2, ...) = (value1, value2, ...)
@ref ~lifetime [:mut] for var in iter
    # body
end

Create a reference to an owned value within a lifetime scope. If :mut is specified, creates a mutable reference. Otherwise, creates an immutable reference. Returns a Borrowed{T} or BorrowedMut{T} that forwards access to the underlying value.

Warning

This will not detect aliasing in the iterator.

source

Types

BorrowChecker.TypesModule.OwnedType
Owned{T}

An immutable owned value. Common operations:

  • Create using @own x = value
  • Access value using @take! (moves) or @take (copies)
  • Borrow using @ref
  • Access fields/indices via .field or [indices...] (returns LazyAccessor)

Once moved, the value cannot be accessed again.

Internal fields (not part of public API):

  • value::T: The contained value
  • moved::Bool: Whether the value has been moved
  • immutable_borrows::Int: Count of active immutable borrows
  • symbol::Symbol: Variable name for error reporting
source
BorrowChecker.TypesModule.OwnedMutType
OwnedMut{T}

A mutable owned value. Common operations:

  • Create using @own :mut x = value
  • Access value using @take! (moves) or @take (copies)
  • Modify by setproperty! or setindex!: x.field = value or x[indices...] = value
  • Borrow using @ref or @ref :mut
  • Access fields/indices via .field or [indices...] (returns LazyAccessor)

Once moved, the value cannot be accessed again.

Internal fields (not part of public API):

  • value::T: The contained value
  • moved::Bool: Whether the value has been moved
  • immutable_borrows::Int: Count of active immutable borrows
  • mutable_borrows::Int: Count of active mutable borrows
  • symbol::Symbol: Variable name for error reporting
source
BorrowChecker.TypesModule.BorrowedType
Borrowed{T,O<:AbstractOwned}

An immutable reference to an owned value. Common operations:

  • Create using @ref lt x = value
  • Access value using @take (copies)
  • Access fields/indices via .field or [indices...] (returns LazyAccessor)

Multiple immutable references can exist simultaneously. The reference is valid only within its lifetime scope.

Internal fields (not part of public API):

  • value::T: The referenced value
  • owner::O: The original owned value
  • lifetime::Lifetime: The scope in which this reference is valid
  • symbol::Symbol: Variable name for error reporting
source
BorrowChecker.TypesModule.BorrowedMutType
BorrowedMut{T,O<:OwnedMut}

A mutable reference to an owned value. Common operations:

  • Create using @ref lt :mut x = value
  • Access value using @take (copies)
  • Access fields/indices via .field or [indices...] (returns LazyAccessor)

Only one mutable reference can exist at a time, and no immutable references can exist simultaneously.

Internal fields (not part of public API):

  • value::T: The referenced value
  • owner::O: The original owned value
  • lifetime::Lifetime: The scope in which this reference is valid
  • symbol::Symbol: Variable name for error reporting
source
BorrowChecker.TypesModule.LazyAccessorType
LazyAccessor{T,P,S,O<:Union{AbstractOwned,AbstractBorrowed}}

A lazy accessor for properties or indices of owned or borrowed values. Maintains ownership semantics while allowing property/index access without copying or moving.

Created automatically when accessing properties or indices of owned/borrowed values:

@own x = (a=1, b=2)
x.a  # Returns a LazyAccessor

Internal fields (not part of public API):

  • parent::P: The parent value being accessed
  • property::S: The property/index being accessed
  • property_type::Type{T}: Type of the accessed property/index
  • target::O: The original owned/borrowed value
source

Traits

BorrowChecker.StaticTraitModule.is_staticFunction
is_static(x)

This trait is used to determine if we can safely @take! a value without marking the original as moved.

This is somewhat analogous to the Copy trait in Rust, although because Julia immutables are truly immutable, we actually do not need to copy on these.

For the most part, this is equal to isbits, but it also includes things like Symbol and Type{T} (recursively), which are not isbits, but which are immutable.

source

Errors

Experimental Features

BorrowChecker.Experimental.@managedMacro
@managed f()

Run code with automatic ownership transfer enabled. Any Owned or OwnedMut arguments passed to functions within the block will automatically have their ownership transferred using the equivalent of @take!.

Warning

This is an experimental feature and may change or be removed in future versions.

source

Internals

Normally, you should rely on OrBorrowed and OrBorrowedMut to work with borrowed values, or use @take and @take! to unwrap owned values. However, for convenience, it might be useful to define functions on Owned and OwnedMut types, if you are confident that your operation will not "move" the input or return a view of it.

Many functions in Base are already overloaded. But if you need to define your own, you can do so by using the request_value function and the AllWrappers type union.

Core Types

  • AllWrappers{T}: A type union that includes all wrapper types (Owned{T}, OwnedMut{T}, Borrowed{T}, BorrowedMut{T}, and LazyAccessor{T}). This is used to write generic methods that work with any wrapped value.

Core Functions

  • request_value(x, Val(:read)): Request read access to a wrapped value
  • request_value(x, Val(:write)): Request write access to a wrapped value

Examples

Here's how common operations are overloaded:

  1. Binary operations (like *) that only need read access:
function Base.:(*)(l::AllWrappers{<:Number}, r::AllWrappers{<:Number})
    return Base.:(*)(request_value(l, Val(:read)), request_value(r, Val(:read)))
end
  1. Mutating operations (like pop!) that need write access:
function Base.pop!(r::AllWrappers)
    return Base.pop!(request_value(r, Val(:write)))
end

The request_value function performs safety checks before allowing access:

  • For read access: Verifies the value hasn't been moved
  • For write access: Verifies the value is mutable and not borrowed

Note that for operations that need write access, and return a view of the input, it is wise to modify the standard output to return nothing instead, which is what we do for push!:

function Base.push!(r::AllWrappers, items...)
    Base.push!(request_value(r, Val(:write)), items...)
    return nothing
end

While this violates the expected return type, it is a necessary evil for safety. The nothing return will cause loud errors if you have code that relies on this design. This is good! Loud bugs are collaborators; silent bugs are saboteurs.