Silk

silk/shared

Explicitly allocated, single-threaded shared ownership with callback-scoped access.

When to use

Use Shared when several independently owned local values or dormant computations must retain one mutable state without retaining a lexical borrow for their whole lifetimes. Construction is the only implicit storage boundary: make allocates once, while clone, with, and withMut allocate nothing themselves.

Details

Every handle is affine and local to one execution thread. Cloning adds one strong obligation; dropping a non-last handle preserves the value and allocation, while dropping the last handle cleans the value and then releases the allocation. Access is callback-scoped and exclusive even through with: nesting with under with, withMut under with, with under withMut, or withMut under withMut through an alias traps before the nested callback receives a reference.

Gotchas

Strong-reference cycles leak until ordinary source breaks them. This module does not expose weak handles, allocation identity, raw addresses, thread transfer, or post-trap cleanup guarantees. Extract work under withMut and invoke external callbacks only after the access call returns.

Examples

Share and update one local value

import silk.allocator { Allocator }

import silk.effect { Effect }

import silk.shared { clone, make, with, withMut }

struct Counter { value: i32 }

fn increment(value: &mut Counter) -> i32 {
  value.value = value.value + 22
  return value.value
}

fn read(value: &Counter) -> i32 { return value.value }

effect fn useCell() -> i32
! Allocator.OutOfMemoryError {
  let mut allocator = Allocator.systemAllocatorProvider()
  let creating = make<Counter>(Counter { value: 20 })
    |> Effect.provideMut<Allocator>(&mut allocator)
  let cell = run creating
  let alias = clone<Counter>(&cell)
  let updated = withMut<Counter, i32>(&alias, increment)
  return with<Counter, i32>(&cell, read)
}

effect fn recover(error: Allocator.OutOfMemoryError) -> i32 {
  return 0
}

pub fn main() -> i32 {
  return run Effect.catchAll(useCell(), recover)
}

Import as Shared with import silk.shared.

Public declarations: 5.

Shared

pub struct Shared<T>

An affine, non-thread-transferable strong handle to one local value.

Details

The opaque core is the sole field and supplies recursively derived cleanup and local execution affinity. Shared declares no source Drop hook.

make

pub effect fn make<T>(value: T) -> silk/shared.Shared<T> ! OutOfMemoryError ? &mut Allocator

Allocates one control block and transfers value into a new strong handle.

Details

Allocation failure leaves value under ordinary Effect-frame cleanup and produces no handle. The returned handle carries no allocator requirement after construction completes.

clone

pub fn clone<T>(self: &silk/shared.Shared<T>) -> silk/shared.Shared<T>

Adds one strong handle without allocating or touching the stored value.

Details

Count exhaustion traps before mutation and does not produce a partial handle.

withMut

pub fn withMut<T, A>(self: &silk/shared.Shared<T>, use: once fn(&mut T) -> A) -> A

Runs one take-once callback with exclusive access to the stored value.

Details

The borrow cannot escape the callback or remain live across suspension. Reentrant access through any alias traps before the nested callback receives a reference.

with

pub fn with<T, A>(self: &silk/shared.Shared<T>, use: once fn(&T) -> A) -> A

Runs one take-once callback with shared access to the stored value.

Details

Inspection delegates through withMut, so it has the same exclusive runtime access state and the same four-way reentrant conflict policy. The callback cannot mutate through its &T.

On this page