Structs & Methods

Course 1 · Ch 5
Structs & Methods
Custom data types with behavior — and finally explaining what String::from has been doing all along

With ownership and borrowing established, this chapter combines fields into custom types and gives them behavior — territory Go's own go2-1 (Structs, Methods, Pointers) already covered, making this a natural place for direct side-by-side comparison.

Defining a Struct

struct Rectangle { width: f64, height: f64, } let rect = Rectangle { width: 30.0, height: 50.0 }; println!("{}", rect.width);

A named collection of typed fields, accessed with dot notation — the same basic shape as a Go struct definition.

impl Blocks

Rust keeps a struct's data definition and its behavior definition separate — fields live in struct, methods live in a separate impl ("implementation") block. This is actually a genuine similarity to Go, not a contrast: Go also separates data (the struct) from behavior (a function with a receiver), unlike Java or Kotlin, which bundle both inside one class body.

impl Rectangle { // methods and associated functions go here }

Methods (&self)

impl Rectangle { fn area(&self) -> f64 { self.width * self.height } } let rect = Rectangle { width: 30.0, height: 50.0 }; println!("Area: {}", rect.area());

A method's first parameter is &self — a borrowed reference to the instance (Chapter 4's & in action), giving read access to its fields without taking ownership. Called with dot syntax: rect.area().

ConceptGoRust
Where methods are definedOutside the struct, with a receiverInside a separate impl block
Receiver syntaxfunc (r Rectangle) Area() float64fn area(&self) -> f64
Calling a methodrect.Area()rect.area()

Associated Functions (No self)

A function inside an impl block without self is an associated function — not a method, and it can't be called on an instance with dot syntax. It's called via Struct::function_name() instead. The most common use is a constructor:

impl Rectangle { fn new(width: f64, height: f64) -> Rectangle { Rectangle { width, height } } } let rect = Rectangle::new(30.0, 50.0);

This is exactly the same :: syntax that's already appeared constantly since Chapter 1 — String::from("hello") is nothing more than a call to an associated function named from defined in String's own impl block. Go has no real language equivalent: a Go "constructor" is just an ordinary function following a naming convention (NewRectangle(width, height float64) Rectangle) — Rust's :: namespacing is a genuine language feature tied directly to the type itself, not a convention.

So *that's* what String::from was doing
Every String::from(...) call since Chapter 1 has been calling String's own from associated function — the exact pattern this chapter's Rectangle::new(...) example just defined from scratch. Nothing about it was ever special syntax; it's just a constructor-style associated function, precisely like the ones you can now write yourself.
self by value consumes the instance
A method can take &self (borrow, read-only), &mut self (borrow, mutable), or plain self (by value) — and that last form genuinely moves the instance into the method, per Chapter 3's move semantics. After calling a method that takes self by value, the original variable is no longer usable at all, exactly like passing it into any other function. Coming from a language where "this"/"self" is always just an implicit reference with no ownership implications, this is an easy trap — reach for &self by default, and use plain self only when a method is deliberately meant to consume the instance (e.g. transforming it into something else).

Coding Challenges

Challenge 1

Define a struct Circle with a single field radius (f64). Write a method area(&self) that returns the circle's area (use 3.14159 for pi), and call it on an instance.

📄 View solution
Challenge 2

Add an associated function Circle::new(radius: f64) -> Circle to Challenge 1's struct, and use it to construct an instance instead of the struct-literal syntax.

📄 View solution
Challenge 3

Explain the difference between a method and an associated function in Rust, and why Struct::new(...) can't be called as instance.new(...) the way area() can be called as instance.area().

📄 View solution

Chapter 5 Quick Reference

  • struct Name { field: Type, ... } — defines a custom data type
  • impl Name { } — a separate block holding methods and associated functions for that struct
  • Method: first parameter is &self/&mut self/self; called with instance.method()
  • Associated function: no self parameter; called with Struct::function() — the pattern behind every String::from(...) call so far
  • self by value moves/consumes the instance — it's unusable afterward, per Chapter 3's move rules
  • Rust and Go both separate data (struct) from behavior — unlike Java/Kotlin's class-bundled approach
  • Rust's :: constructor pattern is a real language feature; Go's NewX() is only a naming convention
  • Next chapter: Enums & Pattern Matching — enums that carry data, the match expression, and Option<T> replacing null entirely