rust :: vs .
When reading rust codes, you sometimes see File::open
or f.read()
. Why are some functions accessed with ::
, and others with .
?
By taking a look at the source code for std::fs::File
, we could see that these functions are defined either in impl File
, or impl SomeTrait for File
:
// src/std/fs.rs snippet
impl File {
pub fn open(path: P) -> io::Result<File> {snip}
pub fn create(path: P) -> io::Result<File> {snip}
pub fn create_new(path: P) -> io::Result<File> {snip}
pub fn metadata(&self) -> io::Result<Metadata> {snip}
}
impl Read for File {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {snip}
}
impl Write for File {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {snip}
}
Either impl File
or impl SomeTrait for File
adds functions to the File struct, but why are they accessed differently?
When to use ::
accessing a struct's associated functions
accessing a crate or a module's functions. we'll come back to this later
What's an associated function?
An associated function is a function that operates on the struct. For example, both File::open()
and File::create()
return a new File instance (technically a File instance wrapped in a Result
enum). Some languages call these functions static methods.
Associated functions are always accessed with a ::
.
The most common example is the new()
function that creates a new instance and initializes its member values:
// A struct about Cats.
struct Cat {
name: String,
age: u8,
}
impl Cat {
// An associated function to create a new cat.
fn new() -> Cat {
Cat{
name: String::from("notadog"),
age: 0,
}
}
}
fn main() {
// Call the associated function to birth a new cat instance.
let mycat = Cat::new();
}
Placing functions in impl Cat
or impl SomeTrait for Cat
makes obvious to anyone reading the code the relationship between the function and Cat
.
When to use .
On the other hand, .
is how you access instance methods. These methods operate on the instance (or object). These methods' first argument is always self
. This is identical to many other languages that support object-oriented programming.
Examples include f.read()
, f.write()
:
use std::fs::File;
let f = File::open("myfile.txt");
f.read(); // <-- calling `read` method on instance `f`
Another scenario that uses ::
is to access items in a crate or module. For example, io.copy()
does not belong to any struct, it's a function defined in the std::io
module.
Take a look at the source code for io::copy()
, you'll see copy.rs module located under src/std/io:
// src/std/io/copy.rs snippet
pub fn copy<R: ?Sized, W: ?Sized>(reader: &mut R, writer: &mut W) -> Result<u64>
To access copy()
, you'd need to use std::io;
or use std::io::copy;
to bring it into scope:
use std::io;
io::copy(r, w);
// or
use std::io::copy;
copy(r, w);
As you can see, the use
keyword also uses ::
to locate io
module in std
crate. Here ::
behaves almost like /
in a path.