Snapshots

Most of the time, we'd like to access data without taking ownership over it. To accomplish this, Cairo uses a snapshot mechanism. Instead of passing objects by value (T), objects can be passed by snapshot (@T).

The compiler statically guarantees that snapshots always point to valid values. Since Cairo's memory is immutable, snapshots are simply views into memory cells at a specific state.

#[derive(Drop)]
struct NonCopyU8 {
    inner: u8,
}

// This function takes ownership of type that is not copyable
fn eat_noncopy_u8(noncopy_u8: NonCopyU8) {
    println!("Dropping a non copyable type that contains {}", noncopy_u8.inner);
}

// This function takes a snapshot of a non-copyable type
fn snapshot_noncopy(snapshot_noncopy_u8: @NonCopyU8) {
    println!("This non-copyable type contains {}", snapshot_noncopy_u8.inner);
}

// This function takes a snapshot of a copyable type
fn snapshot_copyable(snapshot_copyable_u8: @u8) {
    println!("This copyable type contains {}", snapshot_copyable_u8);
}

fn main() {
    // Create a both a copyable and non-copyable type.
    let copyable_u8 = 5_u8;
    let noncopy_u8 = NonCopyU8 { inner: 5_u8 };

    // Take snapshots of the contents. Ownership is not taken,
    // so the contents can be snapshotted again.
    snapshot_noncopy(@noncopy_u8);
    snapshot_copyable(@copyable_u8);

    {
        // Take a snapshot of the data contained inside the box
        let snap_to_noncopy: @NonCopyU8 = @noncopy_u8;

        // This is allowed! Snapshots are immutable views
        // so we can still use noncopy_u8 even while snapshots exist
        eat_noncopy_u8(noncopy_u8);

        // We can still use the snapshot after the original variable is dropped
        // since snapshots are just views of immutable memory cells
        snapshot_noncopy(snap_to_noncopy);
        // `snap_to_noncopy` goes out of scope
    }
    // We can't use noncopy_u8 here since ownership was transferred to eat_noncopy_u8
// eat_noncopy_u8(noncopy_u8); // This would fail to compile
}