Python thinks: "Objects have methods"
Rust thinks: "Functions operate on data with capabilities"
When you see a function: fn foo<T: Read + Seek>(source: &mut T) -> Result<(), Error>
Ask these four questions:
- Data: What data do I have? →
sourceis a mutable reference to typeT - Capabilities: What can this data do? →
TimplementsReadandSeektraits - Flow: How does data flow? →
sourcewill be read and seeked, but not consumed (you keep ownership) - Use next: What do I use after the call?
()return → use modified parameter or modified receiverTreturn → use returned value (bind to variable)
fn function_name(parameter_name: ParameterType) -> ReturnType {
// function body
}- Parameters require explicit types (no inference for fn signatures)
- Return type is always explicit (can be
()for "nothing")
// Signature
fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), Error>
// Usage
let mut buffer = [0u8; 1024];
file.read_exact(&mut buffer)?; // Returns Ok(()) - ignore it
// USE: buffer (now filled with data)// Signature
fn parse<F>(&self) -> Result<F, F::Err>
// Usage
let value = "42".parse::<u32>()?; // Returns Ok(42) or Err
// USE: value (the returned u32)// Signature
fn push(&mut self, value: T) -> ()
// Usage
vec.push(42); // Returns ()
// USE: vec (now contains 42)/// Opens a file handle for reading (does NOT read any bytes yet)
/// Returns a Result<File, Error>
let mut file = File::open(path)?;
/// Creates a stack-allocated array of 1024 bytes, all initialized to 0 of type u8
/// Syntax: [value; count]
let mut header_bytes = [0u8; 1024]; // Array, NOT an iterator!
/// The read pointer starts at position 0 for newly opened files
/// BUT: Always seek explicitly for clarity and robustness
file.seek(SeekFrom::Start(0))?; // Position = 0
/// Reads EXACTLY 1024 bytes from CURRENT position into buffer
/// Advances file position by 1024 bytes
file.read_exact(&mut header_bytes)?;Key principle: seek controls position, read reads from that position into your buffer.
receiver.method_name(arg1, arg2, ...)
│ │ │
│ │ └─ Arguments: Inputs to the method
│ └─ Method name: Which operation to perform
└─ Receiver: The data being operated on (can be self, &self, &mut self)
Critical correction: The receiver does NOT become the return value. They are separate:
receivermay be modified (if&mut self)return valueis separate data (could be(),T, orResult<T, E>)
impl File {
fn do_something(&self) {} // Receiver: immutable borrow
fn modify_self(&mut self) {} // Receiver: mutable borrow (self changes)
fn consume_self(self) {} // Receiver: owned (self is consumed/moved)
}Trait = What you can DO with data
// File has these capabilities
impl Read for File { } // ✅ Can read bytes
impl Seek for File { } // ✅ Can seek positions
impl Write for File { } // ✅ Can write bytes
// Vec has these capabilities
impl Read for Vec<u8> { } // ✅ Can read (from memory)
// ❌ NO Seek - you can't "seek" in memory
// String has this capability
impl Display for String { } // ✅ Can be displayed/printedfn process<T: Read + Seek>(source: &mut T) -> Result<(), Error> {
// Compiler guarantees source can read AND seek
source.seek(SeekFrom::Start(0))?;
source.read(&mut buf)?;
Ok(())
}Mental model: "I need data that can read and seek" (not "I need a File object")
When encountering any function/method:
file.read_exact(&mut buffer)?; // Break it down:
1. Data: &mut file, &mut buffer
2. Capability: File implements Read trait
3. Flow: file → buffer (source to destination)
4. Use next: Returns () → use buffer[0u8; 1024]is an array, not an iterator- Receiver ≠ Return value - they are separate pieces of data
- Always check
&mut- that's what gets modified ()return means the useful data is in parameters/receiver- Skip seek only if you explicitly document the precondition
- Hover in VS Code (quick type info)
F12Go to Definition (see actual signature)- docs.rust-lang.org (full docs with examples)
- Look for
&mutin signature (what gets modified) - Check return type first (what to use next)