Method Syntax
About This Module
This module introduces method syntax in Rust, which brings aspects of object-oriented programming to the language by combining properties and methods in one object. You'll learn how methods are functions defined within the context of a struct and how to use impl blocks to define methods.
Prework
Prework Readings
Read the following sections from "The Rust Programming Language" book:
Pre-lecture Reflections
Before class, consider these questions:
- How do methods differ from regular functions in Rust?
- What is the significance of the
selfparameter in method definitions? - When would you choose to use associated functions vs. methods?
- How do methods help with code organization and encapsulation?
- What are the benefits of the
implblock approach compared to other languages?
Learning Objectives
By the end of this module, you should be able to:
- Define methods within
implblocks for structs - Understand the role of
selfin method definitions - Create associated functions that don't take
self - Use methods to encapsulate behavior with data
- Apply method syntax for cleaner, more readable code
Method Syntax Overview
Brings aspects of object-oriented programming to Rust: combine properties and methods in one object.
Methods are functions that are defined within the context of a struct.
The first parameter is always self, which refers to the instance of the
struct the method is being called on.
Use and impl (implementation) block on the struct to define methods.
struct Point { // stores x and y coordinates x: f64, y: f64, } struct Rectangle { // store upper left and lower right points p1: Point, p2: Point, } impl Rectangle { // This is a method fn area(&self) -> f64 { // `self` gives access to the struct fields via the dot operator let Point { x: x1, y: y1 } = self.p1; let Point { x: x2, y: y2 } = self.p2; // `abs` is a `f64` method that returns the absolute value of the // caller ((x1 - x2) * (y1 - y2)).abs() } fn perimeter(&self) -> f64 { let Point { x: x1, y: y1 } = self.p1; let Point { x: x2, y: y2 } = self.p2; 2.0 * ((x1 - x2).abs() + (y1 - y2).abs()) } } fn main() { let rectangle = Rectangle { p1: Point{x:0.0, y:0.0}, p2: Point{x:3.0, y:4.0}, }; println!("Rectangle perimeter: {}", rectangle.perimeter()); println!("Rectangle area: {}", rectangle.area()); }
Associated Functions without self parameter
Useful as constructors.
You can have more than one impl block on the same struct.
struct Point { // stores x and y coordinates x: f64, y: f64, } struct Rectangle { // store upper left and lower right points p1: Point, p2: Point, } impl Rectangle { // This is a method fn area(&self) -> f64 { // `self` gives access to the struct fields via the dot operator let Point { x: x1, y: y1 } = self.p1; let Point { x: x2, y: y2 } = self.p2; // `abs` is a `f64` method that returns the absolute value of the // caller ((x1 - x2) * (y1 - y2)).abs() } fn perimeter(&self) -> f64 { let Point { x: x1, y: y1 } = self.p1; let Point { x: x2, y: y2 } = self.p2; 2.0 * ((x1 - x2).abs() + (y1 - y2).abs()) } } impl Rectangle { fn new(p1: Point, p2: Point) -> Rectangle { Rectangle { p1, p2 } // instantiate a Rectangle struct and return it } } fn main() { // instantiate a Rectangle struct and return it let rect = Rectangle::new(Point{x:0.0, y:0.0}, Point{x:3.0, y:4.0}); println!("Rectangle area: {}", rect.area()); }
Common Patterns
Builder Pattern with Structs:
struct Config { host: String, port: u16, debug: bool, timeout: u32, } impl Config { fn new() -> Self { Config { host: String::from("localhost"), port: 8080, debug: false, timeout: 30, } } fn with_host(mut self, host: &str) -> Self { self.host = String::from(host); self } fn with_debug(mut self, debug: bool) -> Self { self.debug = debug; self } } fn main() { // Usage let config = Config::new() .with_host("api.example.com") .with_debug(true); }