Browse the docs

silk.mem.pointer.

Raw data pointers: make them, check them for null, move along them, and read and write through them.

When to use#

Use raw pointers at foreign function boundaries and in storage that native code owns. Inside Silk code, use references (&T, &mut T) and slices (&[T]). The compiler checks those, but it does not check raw pointers.

Details#

The pointer types are:

  • *const T and *mut T: one element, never null.

  • [*]const T and [*]mut T: many elements, never null. The pointer has no length.

  • ?*const T, ?[*]mut T, and so on: the same types, but null is allowed.

  • *const align(1) T: a pointer that is not aligned for T. Use Pointer.readUnaligned and Pointer.writeUnaligned with it.

A pointer is Copy. It does not own its target, it does not borrow it, and it does not keep it alive. Making, comparing, and converting pointers is safe. Reading and writing through a pointer is unsafe.

Gotchas#

A pointer that is not null can still point to memory that was released or that is not initialized. Each unsafe operation lists what the caller must prove. If the caller is wrong, the behavior is undefined.

Examples#

Write and read through pointers to a local value and a slice#

silk
import silk.core.option { Option }
import silk.mem.pointer { Pointer }

fn third(values: &[i32]) -> i32 {
  return match move Pointer.nonNullMany<i32>(Pointer.fromSlice<i32>(values)) {
    Option<[*]const i32>.Some { value: first } => unsafe Pointer.read<i32>(
      unsafe Pointer.at<i32>(first, 2)
    )
    Option<[*]const i32>.None => 0
  }
}

pub fn main() -> i32 {
  let mut value: i32 = 1
  let target = Pointer.fromMutRef<i32>(&mut value)
  unsafe {
    Pointer.write<i32>(target, 12)
  }
  let values = [10, 20, 30]
  if value + third(&values) != 42 {
    return 1
  }
  return 0
}

Import as Pointer with import silk.mem.pointer { Pointer }.

Public declarations: 1.

Pointer#

silk
pub struct Pointer

The owner type of the pointer operations. It has no fields and is never constructed.

When to use#

Use raw pointers at foreign function boundaries and in storage that native code owns. Inside Silk code, use references (&T, &mut T) and slices (&[T]). The compiler checks those, but it does not check raw pointers.

Details#

A pointer is Copy. It does not own its target, it does not borrow it, and it does not keep it alive. Making, comparing, and converting pointers is safe. Reading and writing through a pointer is unsafe.

Gotchas#

A pointer that is not null can still point to memory that was released or that is not initialized. Each unsafe operation lists what the caller must prove. If the caller is wrong, the behavior is undefined.

Implementation Pointer#

silk
impl Pointer

Associated function bytes#

silk
pub unsafe fn bytes<T>(pointer: ?*const T) -> ?[*]const u8

Returns pointer as a pointer to bytes. It does not read memory.

When to use#

Use bytes to read the first fields of a foreign record whose full size is not known, without reading the whole T.

Gotchas#

The result is null if pointer is null. It has no length and does not keep the memory alive. Each byte that you read through it must be readable and initialized.

Associated function address#

silk
pub fn address<T>(pointer: ?*const T) -> usize

Returns the address of pointer as a number. Null gives 0.

Details#

It does not read memory. The result has the pointer width of the target. You cannot make a pointer from the result.

Associated function addressMany#

silk
pub fn addressMany<T>(pointer: ?[*]const T) -> usize

Returns the address of pointer as a number. Null gives 0.

Details#

It does not read memory. The result has the pointer width of the target. You cannot make a pointer from the result.

Associated function null#

silk
pub fn null<T>() -> ?*mut T

Returns a null ?*mut T.

Associated function isNull#

silk
pub fn isNull<T>(pointer: ?*const T) -> bool

Returns true if pointer is null.

Associated function isNullMany#

silk
pub fn isNullMany<T>(pointer: ?[*]const T) -> bool

Returns true if the many-element pointer is null.

Associated function nonNull#

silk
pub fn nonNull<T>(pointer: ?*const T) -> Option<*const T>

Returns the non-null form of pointer. Returns an absent option if pointer is null.

Gotchas#

A present result does not prove that the memory is alive or initialized.

Associated function nonNullMut#

silk
pub fn nonNullMut<T>(pointer: ?*mut T) -> Option<*mut T>

Returns the non-null form of pointer. Returns an absent option if pointer is null.

Gotchas#

A present result does not prove that the memory is alive or initialized.

Associated function nonNullMany#

silk
pub fn nonNullMany<T>(pointer: ?[*]const T) -> Option<[*]const T>

Returns the non-null form of pointer. Returns an absent option if pointer is null.

Gotchas#

A present result does not prove that the memory is alive or initialized. The result has no length.

Associated function nonNullManyMut#

silk
pub fn nonNullManyMut<T>(pointer: ?[*]mut T) -> Option<[*]mut T>

Returns the non-null form of pointer. Returns an absent option if pointer is null.

Gotchas#

A present result does not prove that the memory is alive or initialized. The result has no length.

Associated function fromRef#

silk
pub fn fromRef<T>(value: &T) -> *const T

Returns a pointer to the value that value refers to.

Gotchas#

The borrow ends when this call returns. The pointer does not keep the value alive or prevent other access to it. Each later use must make sure that the value is still alive.

Associated function fromMutRef#

silk
pub fn fromMutRef<T>(value: &mut T) -> *mut T

Returns a pointer to the value that value refers to.

Gotchas#

The borrow ends when this call returns. The pointer does not keep the value alive or prevent other access to it. Each later use must make sure that the value is still alive.

Associated function fromSlice#

silk
pub fn fromSlice<T>(values: &[T]) -> ?[*]const T

Returns a pointer to the first element of values.

Gotchas#

The pointer has no length. Pass values.length to the code that uses it. The result type allows null, so use Pointer.nonNullMany before you index it. The pointer does not keep the slice alive.

Associated function fromMutSlice#

silk
pub fn fromMutSlice<T>(values: &mut [T]) -> ?[*]mut T

Returns a pointer to the first element of values.

Gotchas#

The pointer has no length. Pass values.length to the code that uses it. The result type allows null, so use Pointer.nonNullManyMut before you index it. The pointer does not keep the slice alive.

Associated function at#

silk
pub unsafe fn at<T>(pointer: [*]const T, index: usize) -> *const T

Returns a pointer to the element at index of pointer.

Gotchas#

The caller must prove that index is inside the same live allocation. It does not check bounds.

Associated function atMut#

silk
pub unsafe fn atMut<T>(pointer: [*]mut T, index: usize) -> *mut T

Returns a writable pointer to the element at index of pointer.

Gotchas#

The caller must prove that index is inside the same live, writable allocation. It does not check bounds.

Associated function read#

silk
pub unsafe fn read<T: Copy>(pointer: *const T) -> T

Returns a copy of the T at pointer. The pointer must be aligned for T.

Gotchas#

The caller must prove that pointer points to a live, readable, initialized T. For storage that is not aligned, use Pointer.readUnaligned.

Associated function write#

silk
pub unsafe fn write<T: Copy>(pointer: *mut T, value: T) -> ()

Writes value to the T at pointer. The pointer must be aligned for T.

Gotchas#

The caller must prove that pointer points to live, writable storage for a T. For a T that is not Copy, use Pointer.put.

Associated function take#

silk
pub unsafe fn take<T>(pointer: *mut T) -> T

Moves the T out of the storage at pointer and returns it.

Details#

T does not need to be Copy. After the call, the caller owns the value and the storage is uninitialized. Fill it again with Pointer.put, or do not read it again.

Gotchas#

The caller must prove that pointer points to a live, initialized T that no other code will take or drop. Otherwise the value is dropped two times.

Associated function dropInPlace#

silk
pub unsafe fn dropInPlace<T>(pointer: *mut T) -> ()

Drops the T at pointer in place and leaves the storage uninitialized.

When to use#

Use dropInPlace to destroy a value whose storage is released next, such as a heap cell. It runs the drop on the pointee directly. drop Pointer.take(pointer) copies the value out first.

Gotchas#

The caller must prove that pointer points to a live, initialized T that no other code will take or drop. Otherwise the value is dropped two times.

Associated function put#

silk
pub unsafe fn put<T>(pointer: *mut T, value: T) -> ()

Moves value into the storage at pointer. It does not drop the value that was there.

Details#

T does not need to be Copy. The storage owns value after the call.

Gotchas#

The caller must prove that pointer points to live, writable storage for a T. If the storage holds an initialized value, that value is not dropped and leaks.

Associated function borrow#

silk
pub unsafe fn borrow<'a, A, T>(anchor: &'a A, pointer: *const T) -> &'a T

Returns a shared reference to the T at pointer that lives as long as the borrow of anchor.

When to use#

Use it in a handle type that owns storage through a pointer. The handle passes itself as anchor, so the reference cannot outlive the handle. Arc.get works this way.

Gotchas#

The caller must prove that pointer points to an initialized T that stays valid while anchor is borrowed, and that no code changes it during that time, except through atomic operations.

Associated function borrowMut#

silk
pub unsafe fn borrowMut<'a, A, T>(anchor: &'a mut A, pointer: *mut T) -> &'a mut T

Returns an exclusive reference to the T at pointer that lives as long as the exclusive borrow of anchor.

When to use#

Use it in a guard type. The guard passes itself as anchor, so the reference cannot outlive the guard. MutexGuard.get works this way.

Gotchas#

The caller must prove that pointer points to an initialized T that stays valid while anchor is borrowed, and that no other code reads or writes it during that time.

Associated function borrowMany#

silk
pub unsafe fn borrowMany<'a, A, T>(anchor: &'a A, pointer: [*]const T, length: usize) -> &'a [T]

Returns a shared slice of the length elements that start at pointer. The slice lives as long as the borrow of anchor.

When to use#

Use it in a handle type that keeps elements in storage it reaches through a pointer, such as bytes stored inline after a header. The handle passes itself as anchor, so the slice cannot outlive the handle.

Details#

anchor only sets the lifetime of the result. It is not read. The call does not check the pointer or the length, so the result costs the same as a slice that you already have.

Gotchas#

The caller must prove all of these:

  • pointer points to length initialized T values, one after another in one allocation. The pointer must be aligned for T, also when length is 0.

  • length times the size of T fits in isize.

  • The values stay valid while anchor is borrowed, and no code changes them during that time, except through atomic operations.

Examples#

silk
import silk.core.option { Option }
import silk.mem.pointer { Pointer }

struct Inline {
  bytes: [u8; 4],
}

fn view<'a>(cell: &'a Inline) -> &'a [u8] {
  let first = match move Pointer.nonNullMany<u8>(Pointer.fromSlice<u8>(&cell.bytes)) {
    Option<[*]const u8>.Some { value } => value
    Option<[*]const u8>.None => Intrinsic.trap("an array is never at null")
  }
  unsafe { return Pointer.borrowMany<Inline, u8>(cell, first, 3) }
}

pub fn main() -> i32 {
  let cell = Inline { bytes: [1, 2, 3, 4] }
  let bytes = view(&cell)
  if bytes.length != 3 || bytes[2] != 3 {
    return 1
  }
  return 0
}

Associated function borrowManyMut#

silk
pub unsafe fn borrowManyMut<'a, A, T>(anchor: &'a mut A, pointer: [*]mut T, length: usize) -> &'a mut [T]

Returns an exclusive slice of the length elements that start at pointer. The slice lives as long as the exclusive borrow of anchor.

When to use#

Use it in a handle or guard type that keeps elements in storage it reaches through a pointer. The handle passes itself as anchor, so the slice cannot outlive the handle, and nothing else can use the handle while the slice lives.

Details#

anchor only sets the lifetime of the result. It is not read. The call does not check the pointer or the length, so the result costs the same as a slice that you already have.

Gotchas#

The caller must prove all of these:

  • pointer points to length initialized T values, one after another in one allocation. The pointer must be aligned for T, also when length is 0.

  • length times the size of T fits in isize.

  • The values stay valid while anchor is borrowed, and no other code reads or writes them during that time.

Associated function readUnaligned#

silk
pub unsafe fn readUnaligned<T: Copy>(pointer: *const align(1) T) -> T

Returns a copy of the T at pointer. The pointer does not need to be aligned.

Gotchas#

The caller must prove that pointer points to a live, readable, initialized T.

Associated function writeUnaligned#

silk
pub unsafe fn writeUnaligned<T: Copy>(pointer: *mut align(1) T, value: T) -> ()

Writes value to the T at pointer. The pointer does not need to be aligned.

Gotchas#

The caller must prove that pointer points to live, writable storage for a T.

Associated function assumeAligned#

silk
pub unsafe fn assumeAligned<T>(pointer: *const align(1) T) -> *const T

Returns pointer as a pointer that is aligned for T. The address does not change.

Gotchas#

The caller must prove that the address is a multiple of the alignment of T. It does not check.

Associated function assumeMany#

silk
pub unsafe fn assumeMany<T>(pointer: *const T) -> [*]const T

Returns pointer as a many-element pointer. The address does not change.

Gotchas#

The result has no length. Before each indexed access, the caller must prove that the element is inside the allocation.

Associated function assumeAlignedMut#

silk
pub unsafe fn assumeAlignedMut<T>(pointer: *mut align(1) T) -> *mut T

Returns pointer as a pointer that is aligned for T. The address does not change.

Gotchas#

The caller must prove that the address is a multiple of the alignment of T. It does not check.

Associated function assumeManyMut#

silk
pub unsafe fn assumeManyMut<T>(pointer: *mut T) -> [*]mut T

Returns pointer as a many-element pointer. The address does not change.

Gotchas#

The result has no length. Before each indexed access, the caller must prove that the element is inside the allocation.

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.

View the source of this page