0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

The Rust Programming Languageを読み進める(7章)

Last updated at Posted at 2024-05-13

はじめに

The Rust Programming Languageを頭から読み進めてみる。読みながら要点をまとめていった記事です。

7章:肥大化していくプロジェクトをパッケージ、クレート、モジュールを利用して管理する

Cargoの構成は下記の画像のようになっている:

image.png
Cargoプロジェクトの構造 (Quoted from "Practical System Programming for Rust Developers," Link)

  • パッケージ
  • クレート(木枠)
    • Cargo.tomlに定義されているビルドの単位
    • 実行バイナリとライブラリの2つに分かれる
  • モジュール
    • モジュールはクレートの要素に対して階層構造を持たせた仕組み
  • パス
    • モジュールの場所を示すための名前

7.1章:パッケージとクレート

  • パッケージは、ある機能群を提供する1つ以上のクレート
  • パッケージは、各クレートをどの様にビルドするかを定義したCargo.tomlを持つ
  • パッケージを作るコマンドと、作った後のツリーは下記の通り
$ cargo new my-project
.
└── my-project
    ├── Cargo.toml
    └── src
        └── main.rs
[package]
name = "my-project"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
  • my-projectという名前の、バイナリクレートを持っている状態
    • src/main.rssrc/lib.rsがあれば、クレートは2つになる
    • 実行バイナリ
      • エントリポイントを持つ
      • main.rsがクレートルート
    • ライブラリクレート
      • エントリポイントを持たない
      • lib.rsがクレートルート(上記ルートでは定義なし)

7.2章:モジュールを定義して、スコープとプライバシーを制御

  • モジュールはクレートの要素に対して階層構造を持たせた仕組み
  • コードをグループ化することで、可読性と再利用性の向上に貢献
  • 以下はモジュールの定義とモジュールツリーの例
    • modを使って定義し、本体を{}で囲む
    • pub mod xxxpub fn xxxのように書くことで、public/privateの制御も可能
mod front_of_house {
    mod hosting {
        fn add_to_waitlist() {}
        fn seat_at_table() {}
    }

    mod serving {
        fn take_order() {}
        fn serve_order() {}
        fn take_payment() {}
    }
}
crate
 └── front_of_house
     ├── hosting
     │   ├── add_to_waitlist
     │   └── seat_at_table
     └── serving
         ├── take_order
         ├── serve_order
         └── take_payment

7.3章:モジュールツリーの要素を示すためのパス

絶対パスと相対パス

mod front_of_house {
    pub mod hosting {
        pub fn add_to_waitlist() {
            println!("Hi!");
        }
    }
}

pub fn eat_at_restaurant() {
    // 絶対パス
    crate::front_of_house::hosting::add_to_waitlist();
    // 相対パス
    front_of_house::hosting::add_to_waitlist();
}

fn main(){
    eat_at_restaurant();
}
Hi!
Hi!
  • 絶対パスは、crete(ルート)から::でパスを繋いでいく
  • 相対パスは、今のモジュールから::でパスを綱で行く
  • Rustは基本的な要素がデフォルトでPrivate
    • なので、公開する場合はpubキーワードを使う

相対パスをsuperで始める

fn serve_order() {}

pub mod back_of_house {
    pub fn fix_incorrect_order() {
        cook_order();
        super::serve_order();
        println!("fix incorrect order");
    }

    fn cook_order() {
        println!("cook order");
    }
}

fn main(){
    back_of_house::fix_incorrect_order();
}
cook order
fix incorrect order
  • 親モジュールから始まる相対パスは、super`をつけることで実装できる
  • main()では、次の通りfix_incorrect_order()が呼び出される
    1. 内部の関数のcook_orderが呼ばれる
    2. super、つまりback_of_hosueの親モジュールであるcrateを見つけ、serve_orderを探して見つけて呼ぶ
    3. print文が呼ばれる

構造体とenumを公開する

mod back_of_house {
    #[derive(Debug)]
    pub struct Breakfast {
        pub toast: String,
        seasonal_fruit: String,
    }

    #[derive(Debug)]
    pub enum Appetizer {
        Soup,
        Salad,
    }

    impl Breakfast {
        pub fn summer(toast: &str) -> Breakfast {
            Breakfast {
                toast: String::from(toast),
                seasonal_fruit: String::from("peaches"),
            }
        }
    }
}

pub fn eat_at_restaurant() {
    let mut meal = back_of_house::Breakfast::summer("Rye");
    println!("{:?}", meal);

    meal.toast = String::from("Wheat");
    println!("{:?}", meal);

    // pubを付けない限りエラー
    // println!("{:?}", meal.seasonal_fruit);
    // meal.seasonal_fruit = String::from("blueberries");

    println!("{:?}", back_of_house::Appetizer::Soup);
    println!("{:?}", back_of_house::Appetizer::Salad);
}

fn main() {
    eat_at_restaurant();
}
Breakfast { toast: "Rye", seasonal_fruit: "peaches" }
Breakfast { toast: "Wheat", seasonal_fruit: "peaches" }
Soup
Salad
  • 構造体Breakfastpubを付けることで公開できるが、フィールドは非公開のままなので、フィールドごとに公開の有無を決めることが出来る
  • 公開されなかったフィールドは、アクセスすることや書き換えることは不可
  • enumpubで公開すると、フィールドは公開になる
    • 背景としては、1つ1つのバリアントにpubをつけるのが面倒だし、公開されていない方が不変

7.4章:useキーワードでパスをスコープに持ち込む

useを使う

mod front_of_house {
    pub mod hosting {
        pub fn add_to_waitlist() {
            println!("Hi!");
        }
    }
}

use crate::front_of_house::hosting;
use std::collections::HashMap;

pub fn eat_at_restaurant() {
    hosting::add_to_waitlist();
    hosting::add_to_waitlist();
    hosting::add_to_waitlist();
}

fn main() {
    eat_at_restaurant();

    let mut map = HashMap::new();
    map.insert(1, 2);
    println!("{:?}", map);
}
Hi!
Hi!
Hi!
{1: 2}
  • 7.3章ではfront_of_house::hosting::add_to_waitlist();と呼び出しが長かったが、useを使うとシンボリックリンクを定義でき、hosting::add_to_waitlist();だけで済む
  • use crate::front_of_house::hosting::add_to_waitlist;と定義して、add_to_waitlist();端的に呼び出すのは慣例ではない。なぜなら、どこのモジュールの話なのかが分からなくなるため
  • pub use crate::front_of_house::hosting;pub useの定義により外部のコードからアクセスが可能
  • HashMap構造体をスコープに定義しているが、この書き方が慣例

新しい名前をasキーワードで与える

use std::fmt::Result;
use std::io::Result as IoResult;

fn function1() -> Result {
    println!("This is function1!");
    Ok(())
}

fn function2() -> IoResult<()> {
    println!("This is function2!");
    Ok(())
}

fn main() {
    println!("Hello world!");

    if let Err(e) = function1() {
        println!("Error in function1: {:?}", e);
    }

    if let Err(e) = function2() {
        println!("Error in function2: {:?}", e);
    }
}
Hello world!
This is function1!
This is function2!
  • 同じ型を複数使いたい時は、親モジュールを定義する必要がある
    • fn function1() -> Resultfn function2() -> Resultだと区別ができない
    • なのでfn function1() -> fmt::Resultfn function2() -> io::Result<()>のように定義する
  • 上記サンプルのように、asと新しいローカル名(エイリアス)を指定すれば名前の衝突を避けられる

外部のパッケージを使う

use rand::Rng;

fn main() {
    let secret_number = rand::thread_rng().gen_range(1..101);
    println!("rand number: {}", secret_number);
}
rand number: 54
  • Cargo.tomlに追加したクレートはダウンロードされて使用可能になる
  • サンプルの例は、クレートrandRngトレイトをスコープに持ち込み、rand::thread_rng()関数を呼び出している状態

useのネスト

use std::{cmp::Ordering, io};
use std::io::{self, Write};
  • useを使う時に、use std::cmp::Ordering;, use std::io;と書いていくと長くなる
  • 中括弧でネストすることで、宣言文を削減できる

glob演算子

use std::collections::*;

全ての公開要素をスコープに持ち込みたい時は、glo演算子*を続けて書く

7.5章:モジュールを複数のファイルに分割する

mod front_of_house {
    pub mod hosting {
        pub fn add_to_waitlist() {}
    }
}

use crate::front_of_house::hosting;

pub fn eat_at_restaurant() {
    hosting::add_to_waitlist();
    hosting::add_to_waitlist();
    hosting::add_to_waitlist();
}

上記コードについて、下記のように複数のファイルに分割可能

mod front_of_house;

pub use crate::front_of_house::hosting;

pub fn eat_at_restaurant() {
    hosting::add_to_waitlist();
    hosting::add_to_waitlist();
    hosting::add_to_waitlist();
}
pub mod hosting {
    pub fn add_to_waitlist() {}
}
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?