rust :: 與 . 的差別
閱讀 rust 程式的時候偶爾會看到 File::open() 或是 f.read()。但為什麼有些功能是用 :: 呼叫,有些是用 . ?
翻開 std::fs::File 的原始碼,可以看到這些功能都是定義在 impl File 或是 impl SomeTrait for File 內:
// src/std/fs.rs 精簡版
impl File {
pub fn open(path: P) -> io::Result<File> {省略}
pub fn create(path: P) -> io::Result<File> {省略}
pub fn create_new(path: P) -> io::Result<File> {省略}
pub fn metadata(&self) -> io::Result<Metadata> {省略}
}
impl Read for File {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {省略}
}
impl Write for File {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {省略}
}
impl File 或是 impl Read for File 都是在給 File struct 加功能。但既然都是給 File 用,為什麼呼叫方式不一樣?
:: 的使用情境
使用 (access) struct 的相關功能 (associated functions)
使用 (access) 套件 (crate) 或是模組 (module) 內的功能 。這個之後會再解釋
什麼是相關功能 (associated functions)?
Associated function 可以被看作是操作 struct 的功能,例如 File::open() 與 File::create() 都會產生一個新的 File 物件 (嚴格來說是一個 Result enum 包著的 File 物件) 。 有些語言把這種功能稱為 static method 。
只要是呼叫相關功能一律都是用 :: 。
最常見的 associated function 就是 new() , 用來產生一個新的物件並初始化它的內部值。
// 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();
}
把給 Cat 用的功能與方法都放在 impl Cat 或 impl SomeTrait for Cat 區塊內讓讀者很容易地知道這些功能都是跟 Cat 有關。
. 的使用情境
另一方面, . 是呼叫物件方法 (method) 對物件本身操作。它們定義的第一個參數一定都是 self 。 這點跟其他語言的物件方法一致。
只要是呼叫方法一律都是用 . 。
例如 f.read() 、 f.write() 。
use std::fs::File;
let f = File::open("myfile.txt");
f.read(); // <-- 呼叫 f 物件的方法
在解釋 :: 提到的另一個使用情境就是呼叫一個 crate 或 module 內的功能,例如 io::copy() 。這類功能並不隸屬任何 struct ,而是一個模組內的通用功能。
翻開 io::copy() 的原始碼,可以看到 copy() 位於 src/std/io/ 路徑的 copy.rs 模組內:
// src/std/io/copy.rs 精簡
pub fn copy<R: ?Sized, W: ?Sized>(reader: &mut R, writer: &mut W) -> Result<u64>
所以要用 copy 的話就需要 use std::io; 或是 use std::io::copy; 把它匯進來用:
use std::io;
io::copy(r, w);
// or
use std::io::copy;
copy(r, w);
可以看到用 use keyword 的時候也是用 :: 來指定要用的模組。 :: 在這的作用幾乎跟路徑的 / 一樣。