Under heavy development. This site describes the intended 1.0 language. Current features and performance vary. Development status
Sn50SnThe SnAPI programming language
0.2.0 Preview 1 · Frozen documentation

ReadTextAsync

Collects and strictly decodes a complete UTF-8 stream. Validation occurs after EOF, so a Unicode sequence may span short reads. Invalid encoding and allocation/provider failures retain consumed bytes; no replacement decoding, newline conversion or locale behavior is introduced.

Sn is under heavy development. Reference extracted from this release’s bundled standard library. Public API examples are not an exhaustive compatibility test. Read the matching release guide for tested workflows and limitations. Read the status →

Module std.io

import std.io;

Public declaration contracts and authored examples. A page can document several related symbols. Implementation bodies are excluded.

ReadTextAsync

pub async fn ReadTextAsync<S>(Source: mut S&, mut Token: CancellationToken) -> Task<Result<String, StreamReadError>> where S: AsyncReader

Collects and strictly decodes a complete UTF-8 stream. Validation occurs after EOF, so a Unicode sequence may span short reads. Invalid encoding and allocation/provider failures retain consumed bytes; no replacement decoding, newline conversion or locale behavior is introduced. S: Structural AsyncReader source type. Source: Borrowed mutable stream, retained through completion. Token: Cancellation observation passed into byte collection. Return: Task yielding owned immutable text or failure owning the consumed byte prefix. Warning: May allocate and suspend until EOF; text construction copies collected bytes.

Example

module example.async_stream_text;
from std.io import ReadBytesAsync, ReadTextAsync, WriteTextAsync, IoError;
from std.prelude import Result;
from std.task import Task, CancellationToken, YieldAsync;
class Stream {
    pub Input: readonly byte[];
    pub Offset: usize;
    pub Written: usize;
    pub FailAfterTwo: bool;
    pub init(Input: readonly byte[], FailAfterTwo: bool) { this.Input = Input; this.Offset = 0; this.Written = 0; this.FailAfterTwo = FailAfterTwo; }
    pub async fn ReadAsync(Output: mut byte[], mut Token: CancellationToken) -> Task<Result<usize, IoError>> {
        // Match one enum layer at a time while preserving each original fallback.
        switch await YieldAsync(move(Token)) { case .Ok => {} case .Err => return .Err { Error = .Failure { Reason = .Cancelled, Transferred = 0 } }; }
        if Output.Length == 0 || this.Offset == this.Input.Length { return .Ok { Value = 0 }; }
        if this.FailAfterTwo && this.Offset == 2 { Output[0] = this.Input[this.Offset]; this.Offset += 1; return .Err { Error = .Failure { Reason = .Interrupted, Transferred = 1 } }; }
        Output[0] = this.Input[this.Offset]; this.Offset += 1; return .Ok { Value = 1 };
    }
    pub async fn WriteAsync(Input: readonly byte[], mut Token: CancellationToken) -> Task<Result<usize, IoError>> {
        switch await YieldAsync(move(Token)) { case .Ok => {} case .Err => return .Err { Error = .Failure { Reason = .Cancelled, Transferred = 0 } }; }
        if Input.Length == 0 { return .Ok { Value = 0 }; }
        this.Written += 1; return .Ok { Value = 1 };
    }
    pub async fn FlushAsync(mut Token: CancellationToken) -> Task<Result<void, IoError>> { return .Ok; }
}
async fn Main() -> Task<i32> {
    // U+03BB spans two one-byte provider reads; NUL is valid text content.
    let Input: byte[4] = [0xCE_byte, 0xBB_byte, 0_byte, 0x61_byte];
    var Source = Stream(Input, false);
    var Text = switch await ReadTextAsync(Source) { case .Ok { Value = Value } => break Value; case .Err => return 1; };
    if Text.View != "λ\0a" { return 2; }
    if await WriteTextAsync(Source, Text.View) is .Err { Error = _ } { return 3; }
    if Source.Written != 4 { return 4; }
    let Invalid: byte[1] = [0xFF_byte];
    var InvalidSource = Stream(Invalid, false);
    switch await ReadTextAsync(InvalidSource) {
        case .Err { Error = Error } => {
            if Error.Bytes.Length != 1 || Error.Bytes.At(0) != 0xFF_byte { return 5; }
            switch Error.Reason {
                case .Failure { Reason = PatternPayload5014, Transferred = Count } if (PatternPayload5014 is .InvalidUtf8) => { if Count != 1 { return 6; } }
                default => return 7;
            }
        }
        case .Ok => return 8;
    }
    var FailingSource = Stream(Input, true);
    switch await ReadBytesAsync(FailingSource) {
        case .Err { Error = Error } => {
            if Error.Bytes.Length != 3 || Error.Bytes.At(2) != 0_byte || FailingSource.Offset != 3 { return 9; }
            switch Error.Reason {
                case .Failure { Reason = PatternPayload5015, Transferred = Count } if (PatternPayload5015 is .Interrupted) => { if Count != 3 { return 10; } }
                default => return 11;
            }
        }
        case .Ok => return 12;
    }
    return 0;
}

ReadTextAsync

pub fn ReadTextAsync<S>(Source: mut S&) -> Task<Result<String, StreamReadError>> where S: AsyncReader

Supplies the omitted arguments to ReadTextAsync. Note: Defaults: Token = CancellationToken(). S: Same generic argument and constraints as the full operation. Source: Same input, ownership and lifetime contract as the full operation. Return: Exact full-operation result; no additional async frame is created.

Documentation snapshot: October 24, 2026 About versions ↗
Searching Development · language & standard library