Silk

silk/string

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. 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 as Result

import silk.string as String

import silk.usize as usize

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

Build owned text and read its first scalar

import silk.char as char

import silk.allocator { Allocator }

import silk.effect { Effect }

import silk.option as Option

import silk.string as String

import silk.u32 as u32

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

effect fn build() -> i32
! Allocator.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<String.ScalarStep, i32>(move stepped, scalarCode)
  let scalar = Option.unwrapOr<i32>(move mapped, 0)
  return scalar - 191
}

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

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

Import as String with import silk.string.

Public declarations: 22.

String

pub struct String

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

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.

fromUtf8Unchecked

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

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.

fromUtf8

pub fn fromUtf8(values: &[u8]) -> silk/result.Result<string, 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.

make

pub fn make() -> String

Constructs an empty owned String without allocating.

copy

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

Copies valid borrowed text into independently owned storage.

copyUtf8

pub effect fn copyUtf8(values: &[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.

append

pub effect fn append(self: &mut silk/string.String, value: string) -> () ! 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.

appendOwned

pub effect fn appendOwned(self: &mut silk/string.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.

view

pub fn view(self: &silk/string.String) -> string

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

utf8Bytes

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

Borrows a string's immutable UTF-8 encoding.

byteLength

pub fn byteLength(value: string) -> usize

Returns a string's UTF-8 byte length.

ownedUtf8Bytes

pub fn ownedUtf8Bytes(self: &silk/string.String) -> &[u8]

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

ownedByteLength

pub fn ownedByteLength(self: &silk/string.String) -> usize

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

scalarCursor

pub fn scalarCursor() -> ScalarCursor

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

cursorByteOffset

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

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

scalarValue

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

Returns the decoded Unicode scalar value without consuming the step.

scalarByteOffset

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

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

nextCursor

pub fn nextCursor(step: ScalarStep) -> ScalarCursor

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

nextScalar

pub fn nextScalar(value: string, cursor: ScalarCursor) -> 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.

On this page