Silk

silk/string

Profiles: aarch64-apple-darwin, aarch64-unknown-linux-gnu, aarch64-unknown-linux-gnu-no-libc, wasm32-unknown-unknown, x86_64-unknown-linux-gnu, x86_64-unknown-linux-gnu-no-libc.

Valid UTF-8 text, including owned storage, byte validation, and scalar-by-scalar traversal.

When to use

Use the built-in string type for borrowed text and String when text must own its storage. Use Bytes when arbitrary octets must survive without UTF-8 validation.

Details

fromUtf8 validates and borrows existing bytes without allocating; copyUtf8 validates and owns a copy. fromBytes adopts owned bytes without copying; intoBytes transfers them back. append and appendOwned leave the original value unchanged if growth cannot allocate. Scalar cursors expose Unicode scalar values and byte offsets, not grapheme clusters.

Gotchas

A ScalarCursor is meaningful only for the same unchanged string from which its traversal began. Start with scalarCursor and advance only with nextCursor.

Examples

Validate borrowed UTF-8 bytes

import silk.result { Result }

import silk.string { String, ScalarStep, InvalidUtf8 }

import silk.usize

pub fn main() -> i32 {
  let valid = String.fromUtf8(b"Silk")
    |> Result.unwrapOr<string, InvalidUtf8>("")
  let rejected = String.fromUtf8(b"a\x80")
    |> Result.unwrapOr<string, InvalidUtf8>("")
  let length = String.byteLength(valid)
    |> usize.toI32
  let rejectedLength = String.byteLength(rejected)
    |> usize.toI32
  return length + rejectedLength + 38
}

Build owned text and read its first scalar

import silk.char

import silk.allocator { Allocator, OutOfMemoryError }

import silk.effect { Effect }

import silk.option { Option }

import silk.string { String, ScalarStep, InvalidUtf8 }

import silk.u32

fn scalarCode(step: ScalarStep) -> i32 {
  return String.scalarValue(&step)
    |> char.toU32
    |> u32.toI32
}

effect fn build() -> i32
! OutOfMemoryError {
  let mut allocator = Allocator.systemAllocatorProvider()
  let copying = String.copy("é")
    |> Effect.provideMut<Allocator>(&mut allocator)
  let mut text = run copying
  let appending = String.append(&mut text, "!")
    |> Effect.provideMut<Allocator>(&mut allocator)
  let appended = run appending
  let stepped = String.nextScalar(String.view(&text), String.scalarCursor())
  let mapped = Option.map<ScalarStep, i32>(move stepped, scalarCode)
  let scalar = Option.unwrapOr<i32>(move mapped, 0)
  return scalar - 191
}

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

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

Import as String with import silk.string { String }.

Public declarations: 4.

String

pub struct String

An owned sequence of valid UTF-8 bytes that releases its storage on drop.

Associated function String.fromUtf8Unchecked

pub unsafe fn fromUtf8Unchecked<'life0>(values: &'life0 [u8]) -> string<'life0>

Borrows caller-validated UTF-8 bytes as text without runtime validation.

When to use

Use this function only when an earlier operation proves that the complete byte view is UTF-8. Use fromUtf8 when the bytes have not been validated.

Gotchas

The caller must guarantee that the complete byte view is valid UTF-8 for the lifetime of the returned string view. Invalid bytes violate the safety contract.

Associated function String.fromUtf8

pub fn fromUtf8<'life0>(values: &'life0 [u8]) -> silk/result.Result<string<'life0>, silk/string.InvalidUtf8>

Validates a complete byte view and borrows it as text without allocating.

Details

Success returns a string view with the same lexical lifetime as values. Failure returns the first invalid byte offset in InvalidUtf8.

Associated function String.make

pub fn make() -> String

Constructs an empty owned String without allocating.

Associated function String.fromBytes

pub fn fromBytes(values: Bytes) -> silk/result.Result<silk/string.String, silk/string.InvalidUtf8>

Validates owned bytes and transfers their storage into a String without allocation or copying.

When to use

Use when a byte buffer must become owned text. Use fromUtf8 to borrow bytes without consuming them.

Details

This function consumes values. Invalid UTF-8 returns the first invalid byte offset and releases the input storage. Success preserves the input allocation and its capacity.

Associated function String.fromBytesUnchecked

pub unsafe fn fromBytesUnchecked(values: Bytes) -> String

Transfers caller-validated UTF-8 storage into a String without validation, allocation, or copying.

When to use

Use when an earlier operation proves that every initialized byte forms valid UTF-8. Use fromBytes when that guarantee is not available.

Gotchas

The complete initialized contents of values must be valid UTF-8. Invalid bytes violate the safety contract. This function consumes values and preserves its allocation and capacity.

Method String.intoBytes

pub fn intoBytes(self: String) -> Bytes

Consumes the String and returns its bytes without allocation or copying.

Details

The returned bytes retain the allocation, capacity, and complete UTF-8 contents. They can be modified as arbitrary bytes after this conversion.

Associated function String.copy

pub effect<'life0> fn copy<'life0>(value: string<'life0>) -> String ! OutOfMemoryError ? &mut Allocator

Copies valid borrowed text into independently owned storage.

Associated function String.copyUtf8

pub effect<'life0> fn copyUtf8<'life0>(values: &'life0 [u8]) -> silk/result.Result<silk/string.String, silk/string.InvalidUtf8> ! OutOfMemoryError ? &mut Allocator

Validates complete UTF-8 bytes and copies them into independently owned storage.

When to use

Use this function when the bytes must outlive their current buffer. Use fromUtf8 for a borrowed result without allocation.

Details

Invalid input returns InvalidUtf8 as ordinary result data. Allocation failure remains in the Effect failure channel. No owned string is returned in either failure case.

Method String.append

pub effect<'env> fn append<'life0: 'env, 'life1: 'env, 'env>(self: &'life0 mut String, value: string<'life1>) -> () ! OutOfMemoryError ? &mut Allocator

Appends complete valid text atomically with respect to allocation failure.

When to use

Use this function for borrowed text. Use appendOwned when the suffix is an owned String.

Details

If growth fails, self keeps its prior contents and byte length.

Method String.appendOwned

pub effect<'life0> fn appendOwned<'life0>(self: &'life0 mut String, value: String) -> () ! OutOfMemoryError ? &mut Allocator

Appends another owned String atomically with respect to allocation failure.

When to use

Use this function to consume an owned suffix. Use append when the suffix is borrowed text.

Details

This function consumes value. If growth fails, self keeps its prior contents and byte length.

Method String.view

pub fn view<'life0>(self: &'life0 String) -> string<'life0>

Borrows the complete owned contents as valid text without allocating or copying.

Associated function String.utf8Bytes

pub fn utf8Bytes<'life0>(value: string<'life0>) -> &'life0 [u8]

Borrows a string's immutable UTF-8 encoding.

Associated function String.byteLength

pub fn byteLength<'life0>(value: string<'life0>) -> usize

Returns a string's UTF-8 byte length.

Method String.ownedUtf8Bytes

pub fn ownedUtf8Bytes<'life0>(self: &'life0 String) -> &'life0 [u8]

Borrows an owned String's immutable UTF-8 encoding.

Method String.ownedByteLength

pub fn ownedByteLength<'life0>(self: &'life0 String) -> usize

Returns an owned String's initialized UTF-8 byte length.

Associated function String.scalarCursor

pub fn scalarCursor() -> ScalarCursor

Creates a cursor at UTF-8 byte offset zero, before the first Unicode scalar.

Associated function String.cursorByteOffset

pub fn cursorByteOffset<'life0>(cursor: &'life0 silk/string.ScalarCursor) -> usize

Returns a cursor's explicit UTF-8 byte offset.

Associated function String.scalarValue

pub fn scalarValue<'life0>(step: &'life0 silk/string.ScalarStep) -> char

Returns the decoded Unicode scalar value without consuming the step.

Associated function String.scalarByteOffset

pub fn scalarByteOffset<'life0>(step: &'life0 silk/string.ScalarStep) -> usize

Returns the UTF-8 byte offset at which one step begins.

Associated function String.nextCursor

pub fn nextCursor(step: ScalarStep) -> ScalarCursor

Consumes one scalar step and returns the cursor immediately after that scalar.

Associated function String.nextScalar

pub fn nextScalar<'life0>(value: string<'life0>, cursor: ScalarCursor) -> silk/option.Option<silk/string.ScalarStep>

Decodes the scalar at a cursor, or returns None at the end of the string.

Details

A present step contains the scalar, its starting byte offset, and the next cursor. This function does not allocate.

Gotchas

The cursor must come from scalarCursor or nextCursor for the same unchanged string.

InvalidUtf8

pub struct InvalidUtf8

The first byte offset at which UTF-8 validation failed.

Field offset

pub offset: usize

The zero-based offset of the first byte that cannot continue a valid UTF-8 sequence.

ScalarCursor

pub struct ScalarCursor

An opaque UTF-8 position used for scalar-by-scalar traversal.

ScalarStep

pub struct ScalarStep

One decoded Unicode scalar, its byte offset, and the cursor after it.

On this page