/
nv-lang
/
nova
Обзор
Документация
Войти
/
nv-lang
/
nova
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
main
std/src/collections/vec/views.nv
177 строк
8 KB
Evgeniy Golovin
docs(std/src): clean comments batch 15 — collections vec/*, set/core, linkedlist
01 авг 2026, 11:56
01 авг 2026, 11:56
2d0a27f
Код
Авторство
О чём код?
// std.collections.vec (views) — eager zero-copy `[]T`-views. // // Every method here returns a `[]T` ≡ `Vec[T]` view of the SAME type (single // `[]T` type — there is NO separate `Slice` type) sharing the parent // element buffer: an interior pointer at `@data.offset(start)`, `len == cap`. // The GC keeps the parent buffer alive while any view is reachable // (`GC_all_interior_pointers`). // // **Detach-on-resize (Go-model, GC-safe).** A view has `cap == len`, so the // first *reallocating* mutation on it (`push`/`reserve`/`insert` at `cap == // len`) reallocates into a fresh buffer (`@cap`, core.nv) and the view // silently DETACHES — it never overwrites the parent's backing store (the Go // shared-backing footgun is removed without a borrow-checker). Until that // detach point, a `mut`-bound view writes *through* to the parent buffer // (`v[i] = x` / `mut @as_slice`). The detach point is predictable because exact // capacity is honoured (`new().cap(n)`/`@cap(n)` — no pow2 rounding). // // This is the same view model as `@index(Range)` (`v[a..b]`, slice.nv) and the // str linse `bytes() -> ro []u8` — all same-type, zero-copy. An // owning copy is `clone()` / `to_vec()`, never a view. // // `@chunks`/`@chunks_exact`/`@rchunks`/`@windows` are LAZY iterators (Rust-like, // no outer-`Vec` allocation) that yield `[]T` views over this same buffer. They // live in the explicitly-imported lazy module `std/collections/vec_lazy.nv` // (closure-dense bodies → generics-leak, like every adapter) rather than this // prelude `vec/` folder — see that file's "Slice-view iterators" section // (built on the lazy infra). They are intentionally NOT eager here (an eager // form would allocate a `Vec`-of-views and diverge from the Q-iterator-laziness // canon). #prelude(core, runtime, collections, protocols) module collections.vec // ────────────────────────────────────────────────────────────────────────── // Whole-buffer views — `as_slice` // ────────────────────────────────────────────────────────────────────────── /// Read-only zero-copy view of the entire `Vec` as a `[]T` (`cap == len`). /// /// The returned view aliases the parent's element buffer at offset 0; it is the /// `Vec`-side analogue of `str.@bytes()`. Because the view's `cap == len`, a /// reallocating mutation on it detaches it (it never disturbs the parent). Use /// this to pass a `Vec` where a sub-range-style `[]T` view is expected without a /// copy; for an OWNING copy use `clone()`. /// /// The recv-mut companion `mut @slice() -> Self` (below) yields a /// write-through view when called on a `mut`-bound receiver (recv-mut /// overload dispatch — same shape as `@as_ptr` / `mut @as_ptr` in access.nv). /// /// ```nova /// let v = Vec[int].of(1, 2, 3) /// let s = v.slice() /// assert(s.len() == 3 && s[0] == 1) /// ``` export fn Vec[T] @slice() -> Self => { @data, @len, cap: @len } /// Mutable zero-copy view of the entire `Vec` — the writable recv-mut overload /// of `@as_slice`. Selected when the receiver is `mut`-bound; element writes /// (`s[i] = x`, `for mut x in s`) go *through* to the parent buffer until a /// reallocating mutation detaches the view (Go-model, see file header). Named /// `mut @as_slice` (receiver-mut overload), NOT `as_mut_slice` (accessor /// convention, mirroring `mut @as_ptr`). /// /// ```nova /// mut v = Vec[int].of(1, 2, 3) /// mut s = v.slice() /// s[0] = 99 /// assert(v[0] == 99) // write-through to the parent /// ``` export fn Vec[T] mut @slice() -> Self => { @data, @len, cap: @len } // ────────────────────────────────────────────────────────────────────────── // Splitting — split_at / split_first / split_last // ────────────────────────────────────────────────────────────────────────── /// Split into two adjacent zero-copy views at index `i`: `([0, i), [i, len))`. /// Both halves alias the parent buffer (`cap == len` each); no copy. /// /// Contract `0 <= i <= len` — `i == 0` yields `(empty, whole)`, `i == len` /// yields `(whole, empty)`. An out-of-range `i` is a `requires` violation /// (panic), not a clamp: a silent clamp would hide a caller bug and break the /// `len(left) + len(right) == len` invariant. /// /// ```nova /// let v = Vec[int].of(1, 2, 3, 4, 5) /// let (l, r) = v.split_at(2) /// assert(l.len() == 2 && r.len() == 3) /// assert(l[0] == 1 && r[0] == 3) /// ``` export fn Vec[T] @split_at(i int) -> (Self, Self) requires 0 <= i && i <= @len { ro left = Self { @data, len: i, cap: i } ro right = Self { data: unsafe { @data.offset(i) }, len: @len - i, cap: @len - i } (left, right) } /// `Some((first, rest))` — the first element by value and a zero-copy view of /// the remaining `[1, len)` tail — or `None` when empty. The tail view aliases /// the parent buffer (`cap == len`). /// /// ```nova /// let v = Vec[int].of(1, 2, 3) /// match v.split_first() { /// Some((h, t)) => { assert(h == 1 && t.len() == 2 && t[0] == 2) } /// None => { assert(false) } /// } /// assert(Vec[int].new().split_first() == None) /// ``` export fn Vec[T] @split_first() -> Option[(T, Self)] { if @len == 0 { return None } ro head = unsafe { @data.read_at(0) } ro tail = Self { data: unsafe { @data.offset(1) }, len: @len - 1, cap: @len - 1 } Some((head, tail)) } /// `Some((last, init))` — the last element by value and a zero-copy view of the /// leading `[0, len-1)` portion — or `None` when empty. The `init` view aliases /// the parent buffer (`cap == len`). /// /// ```nova /// let v = Vec[int].of(1, 2, 3) /// match v.split_last() { /// Some((l, init)) => { assert(l == 3 && init.len() == 2 && init[1] == 2) } /// None => { assert(false) } /// } /// assert(Vec[int].new().split_last() == None) /// ``` export fn Vec[T] @split_last() -> Option[(T, Self)] { if @len == 0 { return None } ro n = @len - 1 ro last = unsafe { @data.read_at(n) } ro init = Self { @data, len: n, cap: n } Some((last, init)) } // ────────────────────────────────────────────────────────────────────────── // Prefix / suffix views — first_n / last_n // ────────────────────────────────────────────────────────────────────────── /// Zero-copy view of the first `n` elements (`[0, min(n, len))`). CLAMPS rather /// than panics: `n > len` yields the whole `Vec`, `n <= 0` yields an empty view. /// Clamping (not a contract) is the right default for a "take up to N" prefix — /// it mirrors Rust's `[..n.min(len)]` idiom and never surprises a caller asking /// for "at most n". The aliased view has `cap == len`. /// /// ```nova /// let v = Vec[int].of(1, 2, 3, 4, 5) /// assert(v.first_n(3).len() == 3) /// assert(v.first_n(99).len() == 5) // clamped to len /// assert(v.first_n(0).len() == 0) /// ``` export fn Vec[T] @first_n(n int) -> Self { // [M-lint-findings-param-no-contract] ro k = n.clamp(0, @len) { @data, len: k, cap: k } } /// Zero-copy view of the last `n` elements (`[len - min(n, len), len)`). CLAMPS /// rather than panics: `n > len` yields the whole `Vec`, `n <= 0` yields an /// empty view (same rationale as `first_n`). The aliased view has `cap == len`. /// /// ```nova /// let v = Vec[int].of(1, 2, 3, 4, 5) /// assert(v.last_n(2).equal(Vec[int].of(4, 5))) /// assert(v.last_n(99).len() == 5) // clamped to len /// assert(v.last_n(0).len() == 0) /// ``` export fn Vec[T] @last_n(n int) -> Self { // [M-lint-findings-param-no-contract] ro k = n.clamp(0, @len) ro start = @len - k { data: unsafe { @data.offset(start) }, len: k, cap: k } }