API Reference
Ownership Macros
BorrowChecker.MacrosModule.@own
— Macro@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.
BorrowChecker.MacrosModule.@move
— Macro@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.
BorrowChecker.MacrosModule.@clone
— Macro@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.
BorrowChecker.MacrosModule.@take
— Macro@take var
Returns the inner value and does a deepcopy. This does not mark the original as moved.
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.
References and Lifetimes
BorrowChecker.MacrosModule.@lifetime
— Macro@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.
BorrowChecker.MacrosModule.@ref
— Macro@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. See the @lifetime
macro for more information on lifetime scopes.
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.
This will not detect aliasing in the iterator.
BorrowChecker.MutexModule.Mutex
— TypeMutex{T} <: AbstractMutex{T}
A mutex that protects a value of type T. Provides safe concurrent access to the protected value.
Example
m = Mutex([1, 2, 3])
lock(m)
@ref_into :mut arr = m[]
push!(arr, 4)
unlock(m)
BorrowChecker.MacrosModule.@ref_into
— Macro@ref_into [:mut] var = mutex[]
Create a reference to the protected value in a mutex.
If :mut
is specified, creates a mutable reference. Otherwise, creates an immutable reference.
Examples
m = Mutex([1, 2, 3])
lock(m) do
@ref_into :mut arr2 = m[]
push!(arr2, 4)
end
BorrowChecker.MacrosModule.@bc
— Macro@bc func(args...; kwargs...)
Calls func
with the given arguments and keyword arguments, automatically creating temporary borrows for arguments that appear to be owned variables.
Examples
Say that we wish to safely modify an array by reference. We have two owned variables, ar1
and ar2
, and we wish to add the first element of ar2
to ar1
.
@own :mut ar1 = [1, 2]
@own ar2 = [3, 4]
add_first!(x, y) = (x[1] += y[1]; nothing)
If we set up a lifetime scope manually, we might write:
@lifetime lt begin
@ref ~lt :mut ref1 = ar1
@ref ~lt ref2 = ar2
add_first!(ref1, ref2)
end
However, most of the time you only need to create a lifetime scope for a single function call, so @bc
lets us do this automatically:
@bc add_first!(@mut(ar1), ar2)
This will evaluate to something that is quite similar to the manual lifetime scope.
@bc
also supports non-owned variables, which will simply get passed through as-is.
BorrowChecker.MacrosModule.@mut
— Macro@mut expr
Marks a value to be borrowed mutably in a @bc
macro call.
BorrowChecker.MacrosModule.@&
— Macro@&(T)
@&(:mut, T)
Type alias macro for borrowed types. @& T
expands to Union{T, Borrowed{T}}
and @&(:mut, T)
expands to Union{T, BorrowedMut{T}}
(as well as their LazyAccessor
versions).
This is useful for writing generic signatures that accept either a raw value or a borrowed value.
Examples
Here, we define a function that accepts a mutable borrow of a Vector{Int}
. We also demonstrate the use of a Mutex
to protect the vector.
julia> function foo(x::@&(:mut, Vector{Int}))
push!(x, 4)
return nothing
end
foo (generic function with 1 method)
julia> m = Mutex([1, 2, 3])
Mutex{Vector{Int64}}([1, 2, 3])
julia> lock(m) do
@ref_into :mut r = m[]
foo(r)
end
julia> println(m)
Mutex{Vector{Int64}}([1, 2, 3, 4])
Validation
BorrowChecker.MacrosModule.@cc
— Macro@cc closure_expr
"Closure Check" is a macro that attempts to verify a closure is compatible with the borrow checker.
Only immutable references (created with @ref
and @bc
) are allowed to be captured; all other owned and borrowed variables that are captured will trigger an error.
Examples
@own x = 1
@own :mut y = 2
@lifetime lt begin
@ref ~lt z = x
@ref ~lt :mut w = y
# These error as the capturing breaks borrowing rules
bad = @cc () -> x + 1
bad2 = @cc () -> w + 1
# However, you are allowed to capture immutable references
good = @cc () -> z + 1
# This will not error.
end
BorrowChecker.MacrosModule.@spawn
— MacroBorrowChecker.@spawn [options...] expr
Threads.@spawn
but with @cc
applied to the expression to ensure safe captures.
Types
BorrowChecker.TypesModule.AbstractOwned
— TypeAbstractOwned{T}
Base type for all owned value types.
BorrowChecker.TypesModule.AbstractBorrowed
— TypeAbstractBorrowed{T}
Base type for all borrowed reference types.
BorrowChecker.TypesModule.Owned
— TypeOwned{T} <: AbstractOwned{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 valuemoved::Bool
: Whether the value has been movedimmutable_borrows::Int
: Count of active immutable borrowssymbol::Symbol
: Variable name for error reporting
BorrowChecker.TypesModule.OwnedMut
— TypeOwnedMut{T} <: AbstractOwned{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
orx[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 valuemoved::Bool
: Whether the value has been movedimmutable_borrows::Int
: Count of active immutable borrowsmutable_borrows::Int
: Count of active mutable borrowssymbol::Symbol
: Variable name for error reporting
BorrowChecker.TypesModule.Borrowed
— TypeBorrowed{T,O<:AbstractOwned} <: AbstractBorrowed{T}
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 valueowner::O
: The original owned valuelifetime::Lifetime
: The scope in which this reference is validsymbol::Symbol
: Variable name for error reporting
BorrowChecker.TypesModule.BorrowedMut
— TypeBorrowedMut{T,O<:OwnedMut} <: AbstractBorrowed{T}
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 valueowner::O
: The original owned valuelifetime::Lifetime
: The scope in which this reference is validsymbol::Symbol
: Variable name for error reporting
BorrowChecker.TypesModule.LazyAccessor
— TypeLazyAccessor{T,P,S,O<:Union{AbstractOwned,AbstractBorrowed}} <: AbstractWrapper{T}
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 accessedproperty::S
: The property/index being accessedproperty_type::Type{T}
: Type of the accessed property/indextarget::O
: The original owned/borrowed value
BorrowChecker.TypesModule.OrBorrowed
— TypeOrBorrowed{T}
Type alias for accepting either a value of type T
or a borrowed reference to it.
BorrowChecker.TypesModule.OrBorrowedMut
— TypeOrBorrowedMut{T}
Type alias for accepting either a value of type T
or a mutable borrowed reference to it.
Traits
BorrowChecker.StaticTraitModule.is_static
— Functionis_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.
Errors
BorrowChecker.ErrorsModule.BorrowError
— Typeabstract type BorrowError <: Exception end
Base type for all errors related to borrow checking rules.
BorrowChecker.ErrorsModule.MovedError
— TypeMovedError <: BorrowError
Error thrown when attempting to use a value that has been moved.
BorrowChecker.ErrorsModule.BorrowRuleError
— TypeBorrowRuleError <: BorrowError
Error thrown when attempting to violate borrow checking rules, such as having multiple mutable references.
BorrowChecker.ErrorsModule.SymbolMismatchError
— TypeSymbolMismatchError <: BorrowError
Error thrown when attempting to reassign a variable without using proper ownership transfer mechanisms.
BorrowChecker.ErrorsModule.ExpiredError
— TypeExpiredError <: BorrowError
Error thrown when attempting to use a reference whose lifetime has expired.
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}
, andLazyAccessor{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 valuerequest_value(x, Val(:write))
: Request write access to a wrapped value
Examples
Here's how common operations are overloaded:
- 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
- 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.