Skip to main content

Shared types

All Motoko types are divided into sets. The smallest is the set of shared types. Shared types are part of the larger set of stable types.

A shared type's value can be easily exchanged with other actors. To prevent issues associated with sharing mutable state across actors, all shared types are immutable. This immutability allows values to be transmitted safely by copying data, avoiding the complexity and risks of sharing stateful or mutable objects.

Motoko's shared types also have a natural translation to Candid types.

Operationally, you can think of shared types as the subset of types that can be serialized to Candid.

In Motoko, shared functions are used to transmit values between actors. Unlike other functions, their arguments and results are restricted to shared types. This restriction allows the arguments and results to be translated and transmitted as Candid.

These functions are shared because they are also values of shared types and can be communicated to other actors.

Importance of shared types

Shareability is essential for several reasons:

  • Shared types ensure data can be safely serialized and deserialized across network boundaries.
  • Restricting sharing to immutable data prevents synchronization issues between canisters.
  • Shared types map directly to Candid, enabling interaction between canisters written in different languages.
  • Web and mobile frontends communicate with canisters using shared types.

Common shared types

Primitive types

Most primitive types are shared by default.

// Numbers, text, and booleans are shared
let number : Nat = 42;
let message : Text = "Hello IC";
let flag : Bool = true;

Immutable collections

Collections that cannot be modified after creation are shared, including immutable arrays and tuples.

// Immutable arrays are shared
let names : [Text] = ["Motoko", "Ghost", "Astronaut"];

// Tuples of shared types are shared
let person : (Text, Nat) = ("Motoko", 25);

Records with immutable fields

Objects with immutable fields containing shared types are shared, including records.

// Records with immutable fields are shared
let user = {
id = "usr123";
name = "Motoko";
age = 30;
};

Variants with shared type tags

Variant types are shared when their tags contain shared types.

// Variant types with shared tags are shared
type Result = {
#ok : Nat;
#error : Text;
};

let success : Result = #ok(200);
let failure : Result = #error("Operation failed");

Option types

Option types are shared when they contain shared types.

// Option types with shared inner types are shared
let maybeGreeting : ?Text = ?"Hello";
let nothing : ?Nat = null;

Actor references

References to actors are shared, allowing canisters to call each other.

// Actor types are shared
type CounterActor = actor {
increment : shared () -> async Nat;
getValue : shared query () -> async Nat;
};

Shared functions

Function types marked as shared are sharable.

// Shared function types are shared
type Callback = shared (Nat) -> async ();

Non-shared types

Certain types cannot be shared between canisters.

Mutable collections

// Mutable arrays are NOT shared
let mutableArray : [var Nat] = [var 1, 2, 3];

Objects with mutable fields

// Objects with mutable fields are NOT shared
let mutableUser = {
var name = "Motoko";
var age = 30;
};

Error types

// The Error type is NOT shared
let err : Error = Error.reject("Something went wrong");

Objects containing non-shared types

// Objects containing non-shared types are NOT shared
let complex = {
name = "Document";
contents = [var 1, 2, 3]; // Contains a mutable array
};
Logo