@register_passable
You can add the @register_passable
decorator on a struct to tell Mojo that
the type should be passed in machine registers (such as a CPU register; subject
to the details of the underlying architecture). For tiny data types like an
integer or floating-point number, this is much more efficient than storing
values in stack memory. This means the type is always passed by value and
cannot be passed by reference.
The basic @register_passable
decorator does not change the fundamental
behavior of a type: it still needs an __init__()
and __copyinit__()
method
to be copyable (and it may have a __del__()
method, if necessary). For example:
@register_passable
struct Pair:
var a: Int
var b: Int
fn __init__(out self, one: Int, two: Int):
self.a = one
self.b = two
fn __copyinit__(out self, existing: Self):
self.a = existing.a
self.b = existing.b
fn test_pair():
var x = Pair(5, 10)
var y = x
print(y.a, y.b)
y.a = 10
y.b = 20
print(y.a, y.b)
@register_passable
struct Pair:
var a: Int
var b: Int
fn __init__(out self, one: Int, two: Int):
self.a = one
self.b = two
fn __copyinit__(out self, existing: Self):
self.a = existing.a
self.b = existing.b
fn test_pair():
var x = Pair(5, 10)
var y = x
print(y.a, y.b)
y.a = 10
y.b = 20
print(y.a, y.b)
test_pair()
test_pair()
5 10
10 20
5 10
10 20
This behavior is what we expect from Pair
, with or without the decorator.
You should be aware of a few other observable effects:
-
@register_passable
types cannot hold instances of types that are not also@register_passable
. -
@register_passable
types do not have a predictable identity, and so theself
pointer is not stable/predictable (e.g. in hash tables). -
@register_passable
arguments and result are exposed to C and C++ directly, instead of being passed by-pointer. -
@register_passable
types are always trivially movable by transferring the register value around without side effects. As such, they may not define an explicit move constructor.
@register_passable("trivial")
Most types that use @register_passable
are just "bags of bits," which we call
"trivial" types. These trivial types are simple and should be copied, moved, and
destroyed without any custom copy or move constructors, or a destructor. For
these types, you can add the "trivial"
argument, and Mojo synthesizes all the
lifecycle methods except for the constructor:
@fieldwise_init
@register_passable("trivial")
struct Pair:
var a: Int
var b: Int
@fieldwise_init
@register_passable("trivial")
struct Pair:
var a: Int
var b: Int
A trivial type acts like a register-passable type, with a few extra characteristics:
-
Its fields must also be trivial types.
-
The type is trivially copyable. It conforms to the
Copyable
trait, and can be copied by copying the bits from one location to another. As such, the type cannot define an explicit copy constructor. -
The type is trivially movable. It conforms to the
Movable
trait, and can be moved by moving the bits from one location to another. As such, the type cannot define an explicit move constructor. -
The type is trivially destructible. It conforms to the
AnyType
trait and implements a trivial (no-op) destructor. As such, the type cannot define a explicit destructor.
Examples of trivial types include:
- Arithmetic types such as
Int
,Bool
,Float64
etc. - Pointers (the address value is trivial, not the data being pointed to).
- Arrays of other trivial types, including SIMD.
For more information about lifecycle methods (constructors and destructors) see the section about Value lifecycle.
Was this page helpful?
Thank you! We'll create more content like this.
Thank you for helping us improve!