rust :: 與 . 的差別

·

2 min read

閱讀 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 用,為什麼呼叫方式不一樣?

:: 的使用情境

  1. 使用 (access) struct 的相關功能 (associated functions)

  2. 使用 (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 Catimpl 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 的時候也是用 :: 來指定要用的模組。 :: 在這的作用幾乎跟路徑的 / 一樣。