🦔

Rustでクラス定義(?)をファイル(モジュール?)分割する

2022/09/22に公開約2,800字

Rustを勉強していて、最初はmain.rsファイルに全部書いてゴリゴリ動かしてたんですが、Javaの経験からクラス定義は1ファイル1つにすべきだろうと考えRustでのやり方を調べたらハマりました。

main.rs
struct Smartphone {
    price: i64,
    tax: f64,
    service: i64
}

impl Smartphone {
    fn calc(&self) -> i64 {
        // 暗黙的な型変換は行われない
        let price = self.price as f64;
        let tax = self.tax;
        let service = self.service as f64;
        return (price * tax + service) as i64;
    }
}

impl std::fmt::Display for Smartphone {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        return write!(f, "{}, {}, {}", self.price, self.tax, self.service);
    }
}

fn main() {
    let linemo3 = Smartphone { price: 900, tax: 1.1, service: 3 };
    let rakuten20 = Smartphone { price: 1980, tax: 1.1, service: 3 };
    let rakuten_unlimited = Smartphone { price: 2980, tax: 1.1, service: 3 };
    println!("{}", linemo3);
    println!("{}", rakuten20);
    println!("{}", rakuten_unlimited);
    let linemo3 = linemo3.calc();
    let rakuten20 = rakuten20.calc();
    let rakuten_unlimited = rakuten_unlimited.calc();
    println!("linemo3+rakuten20={}", linemo3 + rakuten20);
    println!("linemo3+rakuten_unlimited={}", linemo3 + rakuten_unlimited);
}

これが変更前のmain.rsファイルでメイン関数と構造体を定義してます。何をするプログラムかというとスマホのプラン料金を計算するだけです。この構造体をsmartphone.rsに分割したいと考えました。しかし単にコピペしただけではコンパイラに怒られてしまいます。なぜなら構造体の変数はprivateだから外から呼び出せないのです。構造体やメソッドにpubキーワードを付け、newメソッドをコンストラクタとして定義してそれを呼び出すように対応します。

smartphone.rs
pub struct Smartphone {
    price: i64,
    tax: f64,
    service: i64
}

impl Smartphone {
    pub fn new(price: i64, tax: f64, service: i64) -> Self {
        return Smartphone { price: price, tax: tax, service: service };
    }
    pub fn calc(&self) -> i64 {
        // 暗黙的な型変換は行われない
        let price = self.price as f64;
        let tax = self.tax;
        let service = self.service as f64;
        return (price * tax + service) as i64;
    }
}

impl std::fmt::Display for Smartphone {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        return write!(f, "{}, {}, {}", self.price, self.tax, self.service);
    }
}

そしてmain.rsでは、mod ファイル名; use ファイル名::構造体名を先頭につけることで呼び出せるようになります。これでだいぶほかのオブジェクト指向言語のクラスと同じように見えると思います。

main.rs
mod smartphone;
use smartphone::Smartphone;

fn main() {
    let linemo3 = Smartphone::new(900, 1.1, 3);
    let rakuten20 = Smartphone::new(1980, 1.1, 3);
    let rakuten_unlimited = Smartphone::new(2980, 1.1, 3);
    println!("{}", linemo3);
    println!("{}", rakuten20);
    println!("{}", rakuten_unlimited);
    let linemo3 = linemo3.calc();
    let rakuten20 = rakuten20.calc();
    let rakuten_unlimited = rakuten_unlimited.calc();
    println!("linemo3+rakuten20={}", linemo3 + rakuten20);
    println!("linemo3+rakuten_unlimited={}", linemo3 + rakuten_unlimited);
}

結果

最終的に分割したソースコードはこちらにあります。

https://github.com/rike1019/tostring

Discussion

ログインするとコメントできます