Skip to content
tezvyn:
Menu

Compare Go's []byte and Rust's &[u8]

Source: doc.rust-lang.orgMediumHow cards are made

Tests memory-model depth: Go slices are GC-managed headers (ptr, len, cap) permitting shared mutation, while Rust &[u8] is a borrow-checked fat pointer (ptr, len) enforcing aliasing-XOR-mutation.

What's really being asked

This question probes whether you understand memory representation below the surface syntax and how language design choices shape what a slice actually is. Interviewers want to see that you know Go slices are runtime values with headers while Rust slices are borrow-checked views tied to lifetimes.

A GOOD ANSWER COVERS four things in order. First, memory layout: a Go slice like []byte is a three-word struct containing a pointer to backing array data, a length, and a capacity. A Rust &[u8] is a fat pointer, two words wide, holding a pointer and a length with no capacity because it does not own or manage the backing storage. Second, ownership and lifetime: Go relies on garbage collection, so multiple slices can point to the same backing array and mutate it concurrently without compile-time restriction; Rust enforces aliasing XOR mutation through the borrow checker, meaning a shared slice cannot mutate data and cannot outlive the owner. Third, safety mechanisms: both languages perform runtime bounds checking on indexing, but Rust adds compile-time lifetime and exclusivity guarantees that prevent use-after-free and data races on slice data in safe code. Fourth, re-slicing and growth: Go slices support append and re-slicing within capacity, potentially causing confusing aliasing when underlying arrays overlap; Rust slices are fixed views unless you reborrow a sub-slice, and mutation requires a mutable reference.

The mistakes people make

Claiming Rust slices are just pointers without mentioning fat pointer layout or lifetime parameters. Saying Go slices are fat pointers rather than header structs. Asserting that Rust does not do bounds checking. Ignoring capacity as a distinguishing field in Go. Treating Rust &[u8] as equivalent to a Go slice in terms of ownership, when Rust explicitly borrows rather than owns or references GC-managed memory.

What usually comes next

How does append in Go interact with overlapping slices? What happens when you pass a slice to a goroutine versus a Rust slice across threads? How do string slices differ, such as Go string headers versus Rust string slices? Why can Go have nil slices but Rust slices cannot be null?

A concrete example

Imagine two variables created from the same backing array. In Go, both are independent headers pointing at the same data; writing through one after slicing from the other affects the second if they overlap, and the GC keeps the whole backing array alive as long as any slice references any element. In Rust, you cannot hold a mutable reference to data while any shared reference exists, so overlapping mutable views are impossible in safe code, and the borrow checker ensures the backing storage lives at least as long as any borrowed slice.

Interview question

Which statement accurately contrasts Go's []byte and Rust's &[u8] in terms of memory layout and aliasing rules?

  • a.Go's []byte is a three-word struct with pointer, length, and capacity, allowing multiple slices to mutate the same backing array, while Rust's &[u8] is a two-word fat pointer enforcing shared immutability or exclusive mutability.Correct
  • b.Both are fat pointers with pointer and length fields; Go adds garbage collection to manage backing arrays, while Rust adds compile-time borrow checking.
  • c.Rust's &[u8] stores pointer, length, and capacity in a three-word header, while Go's []byte is a two-word fat pointer that uses runtime bounds checking instead of borrow checking.
  • d.Both slices support append and re-slicing within capacity, but Rust requires a mutable reference to grow the backing array while Go relies on garbage collection.
Why?

Go's []byte is a three-word header struct that permits shared mutable aliases because it is GC-managed, whereas Rust's &[u8] is a two-word fat pointer whose borrow checker enforces aliasing XOR mutation. Option B is tempting because it correctly notes GC versus borrow checking, but it incorrectly claims Go slices are two-word fat pointers, omitting the crucial capacity field and header-struct nature.

Just read this? Test yourself on what you have been reading.

Read the original → doc.rust-lang.org

Put your scrolling time to good use

Learn one idea, try a quiz and save useful cards for revision. Tezvyn makes it easy to learn and stay current in your tech field, a few minutes at a time.

The iPhone app is on the way

We are building it. Until it lands, nothing here is held back from you: every interview card, your saved cards, streaks and the job board all work in Safari, plus hundreds of free practice quizzes of thirty questions each. Sign in and it all carries over to the app the day it arrives.

Want it as an icon? Tap Share at the bottom of Safari, then Add to Home Screen. It opens full screen and the cards you have read stay available offline.

Get it on Google PlayiPhone app coming soon

We are hiring for this. Open roles that interview on go — each one lists the topics its interview covers.

See open roles