# Data types > bytes, lists, maps and structs. ## bytes ```slang let b = b"raw\x00bytes"; // binary-safe literal; \0 \xHH escapes println(len(b)); // byte count, not strlen println(b[0]); // indexing yields an int (0..255) b[0] = 65; // mutable in place let head = b[..2]; // slicing: b[a..b], b[..n], b[n..], b[..] let both = b"ab" + b"cd"; // concatenation if b == other { ... } // content equality via == for byte in b { ... } // iterate byte values ``` `bytes` values carry an explicit length and may contain NULs — safe for network buffers and binary formats. ## Lists [T] ```slang let xs = [10, 20, 30]; // inferred [int] let empty: [str] = []; // empty lists need an annotation push(xs, 40); // grow (amortized O(1)) println(pop(xs)); // shrink from the end xs[0] = 5; // bounds-checked index assignment for x in xs { println(x); } // iteration let ys = xs[0..2] + xs[1..]; // slicing + concatenation let grid = [[1, 2], [3, 4]]; // nested lists ``` Indexing is bounds-checked at runtime; violations abort with a clear message. ## Maps map[K]V ```slang let scores: map[str]int = {"alice": 90, "bob": 85}; scores["carol"] = 78; // insert or overwrite println(scores["alice"]); // lookup (missing key = runtime error) println(len(scores)); // entry count if has(scores, "dave") { ... } // membership test (no error) del(scores, "bob"); // removal let empty: map[int]str = {}; // empty maps need an annotation for k, v in scores { // iteration in insertion order println(k + ": " + to_str(v)); } ``` Keys may be any integer type, `str`, or `bool`; values may be any type, including structs and lists. Backed by an open-addressing hash table (FNV-1a) that keeps entries in insertion order and grows automatically at 75% load. ## Structs ```slang struct Point { x: int, y: int, } impl Point { fn sum(self: Point) -> int { return self.x + self.y; } fn moved(self: Point, dx: int, dy: int) -> Point { return Point { x: self.x + dx, y: self.y + dy }; } } let p = Point { x: 3, y: 4 }; println(p.sum()); // method call; self passed implicitly p.x = 10; // field mutation let q = p.moved(1, 2); // methods can build and return structs struct Rect { tl: Point, br: Point, } let r = Rect { tl: Point { x: 0, y: 0 }, br: Point { x: 4, y: 5 } }; println(r.tl.y); // nested field chains r.br.x = 6; let pts: [Point] = [p, q]; // structs compose with lists & maps push(pts, r.tl); ``` Struct literals must supply every field exactly once, with types checked. Methods live in top-level `impl Name { ... }` blocks; mark a method `pub fn` to export it to importing packages. Structs are values: assignment copies, including any `str` / list / map / `opt` / `result` / `gc struct` fields (shallow — the heap objects are shared). Use `gc struct` when the record itself should be a shared heap object. `own T` is uniquely owned: assignment and passing **move**, and use-after-move is a compile error. A moved binding can be reinitialized. `own` is freed when its binding goes out of scope unless it was moved. --- slang is built and maintained by **Dolphlabs** (Dolph Tech Limited) — https://dolphlabs.com