wira@portfolio ~ /ja/blog/getting-started-with-rust
Go開発者のためのRust入門

Go開発者のためのRust入門

2023年6月15日 Wira

Go開発者のためのRust入門

長年Goの開発者として、最近Rustを探求する機会がありました。この記事では、これら二つの強力な言語間の移行における私の経験を共有し、類似点、相違点、そして実用的なヒントを紹介します。

なぜRustなのか?

GoとRustは両方とも現代的なシステムプログラミング言語ですが、異なるニーズに対応します:

  • Goはシンプルさと読みやすさを重視したネットワークサービスの構築に優れています
  • Rustはガベージコレクタなしでパフォーマンス、メモリ安全性、きめ細かい制御を優先します

メモリ管理:大きな違い

GoからRustに移行する際の最も大きな調整点は、Rustの所有権システムを理解することです:

fn main() {
    // 変数バインディング
    let s1 = String::from("hello"); // s1はこの文字列を所有します
    
    // これはs2に所有権を移動し、s1はもう有効ではありません!
    let s2 = s1;
    
    // これはコンパイルエラーを引き起こします
    // println!("s1: {}", s1);
    
    // これは問題なく動作します
    println!("s2: {}", s2);
}

Goでは、誰が変数を「所有」しているかを気にせずに変数を使用します。ガベージコレクタがメモリのクリーンアップを処理します。Rustのアプローチは多くのバグを排除しますが、より慎重な思考が必要です。

エラー処理アプローチ

Goのエラー処理は明示的かつシンプルです:

file, err := os.Open("file.txt")
if err != nil {
    log.Fatal(err)
}
// fileを使用

RustはResult型を使用してエラー処理を強制します:

let file = match File::open("file.txt") {
    Ok(file) => file,
    Err(error) => {
        panic!("ファイルを開く際に問題が発生しました: {:?}", error);
    },
};

または?演算子を使用してより簡潔に:

fn read_file() -> Result<String, io::Error> {
    let mut file = File::open("file.txt")?;
    let mut contents = String::new();
    file.read_to_string(&mut contents)?;
    Ok(contents)
}

並行処理モデル

Goのゴルーチンとチャネルは美しくシンプルです:

func main() {
    messages := make(chan string)
    
    go func() {
        messages <- "ゴルーチンからこんにちは!"
    }()
    
    msg := <-messages
    fmt.Println(msg)
}

Rustはコンパイル時にデータ競合を防ぐ、型安全な複数のオプションを提供します:

use std::thread;

fn main() {
    let handle = thread::spawn(|| {
        "スレッドからこんにちは!"
    });
    
    let result = handle.join().unwrap();
    println!("{}", result);
}

結論

GoからRustを学ぶことで、全体的により良いプログラマーになりました。所有権の概念は最初に開発速度を落とすかもしれませんが、コードのメモリパターンについて深く考えることを強制します。

Rustに取り組むGo開発者には以下をお勧めします:

  1. ボローチェッカーと戦うのではなく、完全に受け入れること
  2. 小さく、焦点を絞ったプロジェクトから始めること
  3. 優れたcargoツールを活用すること
  4. 「The Book」(Rustプログラミング言語)を読むこと

あなたもGoからRustへの移行を経験しましたか?コメント欄でぜひあなたの経験をお聞かせください。