The Raw Interface

What the OS actually gives you: file descriptors, read(), write(), seek() — just bytes at offsets.

data.bin — 32 bytes

cursor: 0

0
4
8
12
16
20
24
28
52R
75u
73s
74t
00·
00·
00·
00·
41A
6cl
69i
63c
65e
00·
00·
00·
1e·
00·
00·
00·
00·
00·
00·
00·
00·
00·
00·
00·
00·
00·
00·
00·

Click any byte to move the cursor there

Operation log

No operations yet — run seek, read, or write

// Rust
file.seek(SeekFrom::Start(0))?;

That's all you get

The OS gives you exactly 3 operations:

  • seek(n) — move the cursor
  • read(n) — read n bytes
  • write(bytes) — write bytes

No read_string(). No read_u32(). Just bytes.

Key Insight

A file is nothing but a Vec<u8> with a cursor. The OS doesn't know if byte 5 is part of a string, an integer, or a timestamp. It just stores and retrieves bytes at offsets. All meaning lives in your code, not the file.

Notice in the operation log: read(4) returns [52 75 73 74]. Is that the number 1920234836? The string "Rust"? A timestamp? You cannot know without a schema.

This is the fundamental problem: how do you impose structure on a flat byte sequence? That's exactly what framing, encoding, and codecs solve — the next topics.