Index
- Guessing Game
- Common Programming Concepts
- Understanding Ownership
- Using Structs
- Enums and Pattern Matching
- Managing Growing Projects with Packages, Crates, and Modules
- Defining Modules to Control Scope and Privacy
- Paths for Referring to an Item in the Module Tree
- Bringing Paths into Scope with the use Keyword
- Separating Modules into Different Files
- Common Collections
- Error Handling
- Generic Types, Traits, and Lifetimes
- Writing Automated Tests
- Object Oriented Programming
- Adding dependancies
- Option Take
- RefCell
- mem
- Data Structure
- Recipe
- Semi colon
- Calling rust from python
- Default
- Crytocurrency With rust
- Function chaining
- Question Mark Operator
- Tests with println
- lib and bin
- Append vector to hash map
- Random Number
- uuid4
- uwrap and option
- Blockchain with Rust
- Near Protocol
- Actix-web
Method Syntax
Method Syntax
Methods are similar to functions: they’re declared with the
fn
keyword and their name, they can have parameters and a return value, and they contain some code that is run when they’re called from somewhere else.However, methods are different from functions in that they’re defined within the context of a struct (or an enum or a trait object), and their first parameter is always
self
, which represents the instance of the struct the method is being called on.struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
}
fn main() {
let rect1 = Rectangle {
width: 30,
height: 50,
};
println!(
"The area of the rectangle is {} square pixels.",
rect1.area()
);
}
width: u32,
height: u32,
}
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
}
fn main() {
let rect1 = Rectangle {
width: 30,
height: 50,
};
println!(
"The area of the rectangle is {} square pixels.",
rect1.area()
);
}
To define the function within the context of
Rectangle
, we start an impl
(implementation) block. Then we move the area
function within the impl
curly brackets and change the first (and in this case, only) parameter to be self
in the signature and everywhere within the body.In the signature for
area
, we use &self
instead of rectangle: &Rectangle
because Rust knows the type of self
is Rectangle
due to this method’s being inside the impl Rectangle
contextWe’ve chosen
&self
here for the same reason we used &Rectangle
in the function version: we don’t want to take ownership, and we just want to read the data in the struct, not write to it. If we wanted to change the instance that we’ve called the method on as part of what the method does, we’d use &mut self
as the first parameter.Here’s how it works: when you call a method with
object.something()
, Rust automatically adds in &
, &mut
, or *
so object
matches the signature of the method. In other words, the following are the same:p1.distance(&p2);
(&p1).distance(&p2);
(&p1).distance(&p2);
Methods with More Parameters
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
fn can_hold(&self, other: &Rectangle) -> bool {
self.width > other.width && self.height > other.height
}
}
fn main() {
let rect1 = Rectangle {
width: 30,
height: 50,
};
let rect2 = Rectangle {
width: 10,
height: 40,
};
let rect3 = Rectangle {
width: 60,
height: 45,
};
println!("Can rect1 hold rect2? {}", rect1.can_hold(&rect2));
println!("Can rect1 hold rect3? {}", rect1.can_hold(&rect3));
}
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
fn can_hold(&self, other: &Rectangle) -> bool {
self.width > other.width && self.height > other.height
}
}
fn main() {
let rect1 = Rectangle {
width: 30,
height: 50,
};
let rect2 = Rectangle {
width: 10,
height: 40,
};
let rect3 = Rectangle {
width: 60,
height: 45,
};
println!("Can rect1 hold rect2? {}", rect1.can_hold(&rect2));
println!("Can rect1 hold rect3? {}", rect1.can_hold(&rect3));
}
Associated Functions
Another useful feature of
impl
blocks is that we’re allowed to define functions within impl
blocks that don’t take self
as a parameter. These are called associated functions because they’re associated with the struct. They’re still functions, not methods, because they don’t have an instance of the struct to work with. You’ve already used the String::from
associated function.A square
Rectangle
impl Rectangle {
fn square(size: u32) -> Rectangle {
Rectangle {
width: size,
height: size,
}
}
}
fn square(size: u32) -> Rectangle {
Rectangle {
width: size,
height: size,
}
}
}
To call this associated function, we use the
::
syntax with the struct name; let sq = Rectangle::square(3);
is an example. This function is namespaced by the struct: the ::
syntax is used for both associated functions and namespaces created by modules.Multiple
impl
Blocks
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
}
impl Rectangle {
fn can_hold(&self, other: &Rectangle) -> bool {
self.width > other.width && self.height > other.height
}
}
fn area(&self) -> u32 {
self.width * self.height
}
}
impl Rectangle {
fn can_hold(&self, other: &Rectangle) -> bool {
self.width > other.width && self.height > other.height
}
}
There’s no reason to separate these methods into multiple
impl
blocks here, but this is valid syntax.