Structural Go interfaces versus nominal Rust traits
grasp of typing models and their design impact.
Go interfaces are satisfied implicitly by method shape (structural); Rust traits must be explicitly implemented (nominal).
WHAT THIS TESTS It examines whether you understand the difference between structural and nominal interface conformance and can connect it to real API design and testability decisions.
A GOOD ANSWER COVERS In Go, a type satisfies io.Reader simply by having a method Read(p []byte) (int, error); there is no declaration that the type implements the interface, so conformance is structural and implicit. This lets types from completely unrelated packages interoperate and makes it trivial to define small interfaces at the point of use. In Rust, a type only satisfies std::io::Read if it has an explicit impl Read for T block; conformance is nominal and intentional. The orphan rule means you can implement a trait for a type only if you own the trait or the type, which prevents conflicting implementations across crates. Both models let you accept the abstraction as a parameter and substitute fakes.
IMPLICATIONS FOR API DESIGN Go encourages tiny consumer-defined interfaces and easy decoupling but offers weaker discoverability of who implements what. Rust makes implementations explicit and greppable and lets the compiler reason about coherence, at the cost of needing newtype wrappers to extend foreign types.
IMPLICATIONS FOR TESTING In Go you stub a reader by defining any struct with a Read method, often inline. In Rust you implement Read for a test struct, or commonly just use std::io::Cursor over a byte slice or a Vec as a writer.
LIKELY FOLLOW-UPS What is the orphan rule and why does it exist? How do small interfaces in Go affect mocking? When do you reach for the newtype pattern in Rust?
ONE CONCRETE EXAMPLE To test a function that reads from io.Reader in Go, pass strings.NewReader("data") or a custom struct whose Read returns canned bytes; no registration needed. In Rust, to test a function taking impl Read, pass Cursor::new(b"data") which already implements Read, or define a struct and write impl Read for it. The structural side conforms by shape; the nominal side conforms because someone wrote the impl.
Read the original → langindex.dev
Get five bites like this every day.
Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.