silk.mem.output.
Owned storage for one Copy value that a foreign function writes through a pointer.
When to use#
Use this module to call a foreign function that returns its result through an output pointer,
such as int get(T* out). Make an Uninitialized value, pass Uninitialized.address to
the function, and then call Uninitialized.assumeInitialized and Initialized.into.
Details#
Each state owns one allocation. The type tells you if the value is initialized.
Uninitialized.initialize and Uninitialized.assumeInitialized consume the uninitialized
state. Initialized.into consumes the initialized state, returns the value, and releases the
allocation. A state that is dropped releases its allocation and does not read the value.
Gotchas#
The address does not keep the storage alive. The foreign function must not keep the address
after it returns. Uninitialized.assumeInitialized is unsafe, because only the caller knows
that the foreign function wrote a complete, valid value.
Examples#
Let a function write the result through a pointer#
import silk.mem.allocator { Allocator, OutOfMemoryError }
import silk.core.effect { Effect }
import silk.mem.output { Initialized, Uninitialized }
import silk.mem.pointer { Pointer }
// This function stands in for a foreign function such as `void answer(int32_t* out)`.
fn answer(out: *mut i32) -> () {
unsafe {
Pointer.write<i32>(out, 42)
}
}
pub effect fn main() -> i32
! OutOfMemoryError {
let mut allocator = Allocator.systemAllocatorProvider()
let mut storage = run Uninitialized<i32>.make() |> Effect.provideMut<Allocator>(&mut allocator)
answer(Uninitialized<i32>.address(&mut storage))
let filled = unsafe Uninitialized<i32>.assumeInitialized(move storage)
if Initialized<i32>.into(move filled) != 42 {
return 1
}
return 0
}Import as Uninitialized with import silk.mem.output { Uninitialized }.
Public declarations: 2.
Uninitialized#
pub struct Uninitialized<T: Copy>Owned storage for one T that does not hold a valid value yet.
When to use#
Use it to call a foreign function that returns its result through an output pointer, such as
int get(T* out). Pass Uninitialized.address to the function, and then call
Uninitialized.assumeInitialized and Initialized.into.
Details#
It owns one allocation. Dropping it releases the allocation and does not read the value.
Initialized#
pub struct Initialized<T: Copy>Owned storage that holds one valid T. Initialized.into returns it.
Implementation Uninitialized<T>#
impl<T> Uninitialized<T>Associated function make#
pub effect fn make<T: Copy>() -> Uninitialized<T> ! OutOfMemoryError ? &mut AllocatorAllocates storage for one T. It does not write a value.
Errors#
OutOfMemoryErrorif theAllocatorcannot supply the storage.
Method address#
pub fn address<T: Copy>(self: &mut Self) -> *mut TReturns the address of the storage, to pass to a foreign function. It does not read or write the storage.
Gotchas#
The address does not keep the storage alive. The foreign function must finish with it before
self is consumed or dropped.
Method initialize#
pub fn initialize<T: Copy>(self: Self, value: T) -> Initialized<T>Writes value into the storage and returns the initialized state.
Method assumeInitialized#
pub unsafe fn assumeInitialized<T: Copy>(self: Self) -> Initialized<T>Returns the initialized state after foreign code wrote the value.
Gotchas#
The caller must prove that the storage holds a complete, valid T. A partial write, bytes
that were not written, or a value that is not valid for T, such as a bool other than 0 or
1, gives undefined behavior.
Implementation Initialized<T>#
impl<T> Initialized<T>Method into#
pub fn into<T: Copy>(self: Self) -> TReturns the value and releases the storage.
Category: Memory.
Profiles: aarch64-apple-darwin, aarch64-unknown-linux-gnu, aarch64-unknown-linux-gnu-no-libc, x86_64-unknown-linux-gnu, x86_64-unknown-linux-gnu-no-libc, wasm32-unknown-unknown, wasm32-wasip1, wasm32-wasip2.