Open12

Rust学習備忘録

MasaHeroMasaHero

2021/12/04

学習資料

  • 基礎から学ぶ組み込みRust
  • プログラミング言語 Rust 公式ガイド

用語

  • Cargo
  • TOML
  • rustc (=Compiler)
  • Macro
  • クレート
  • モジュール

Cargo

Rustのビルドシステム兼パッケージマネージャである。

新しいプロジェクトを作成する

$ cargo new hello_cargo --bin
$ cd hello
$ ls -al
 .git/
 .gitignore
 Cargo.toml
 src/

Cargoプロジェクトをビルド

下記コマンドでビルド実行する。

$ cargo build

ビルドが成功したら、下記の名前の実行体が生成される。

target/debug/hello_cargo

Windowsなら

target/debug/hello_cargo.exe

Cargoプロジェクトを実行する

下記コマンドでビルドを行い、実行体が生成出来たら実行する。

$ cargo run

Cargo.toml

Cargoの設定ファイル。TOMLフォーマットで記述されている。

$ cat Cargo.toml 
[package]
name = "hello"
version = "0.1.0"
authors = ["<GitUserName> <hogehoge@foo.com>"]
edition = "2018"

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

[dependencies]

マクロ

// 改行付きの標準出力を行うマクロ
println!(); 

変数

let (immutable)

基本的に、Rustの変数はイミュータブル(値が不変)である。(*1)

値を変えたい場合は、後述するmutを使う。

// イミュータブル変数を宣言し初期化する
let var_a = 47;

// 下記の様にイミュータブル変数に代入するとコンパイルエラーを吐く
var_a = 23;

mut (mutable)

letの後ろにmutを付けることで ミュータブル変数となり代入が可能となる。

mutを付けない限り、ミュータブル変数となることはない。(*1)

// ミュータブル変数を宣言し初期化する
let mut var_b = 47;

//コンパイルが通る
var_b = 23;

const(定数宣言)

定数は必ず型定義をしなければならない。

定数名は全て大文字とするのが慣例である。

// ミュータブル変数を宣言し初期化する
const MAGIC_NUMBER: u8 = 51;

static(グローバル変数宣言)

定数と同じく必ず型定義をしなければならない。

グローバル変数名も全て大文字とするのが慣例である。

グローバル変数は、メモリアクセスが安全ではないのでunsafeブロックで囲う必要がある。(後述)

// ミュータブル変数を宣言し初期化する
static mut GO_FLAG: bool = true;

学習の振り返り

Y(やったこと): Rustの学習
W(わかったこと):Rustの基礎的用語・各変数宣言の方法・Cargoコマンド
T(次回やること):Rustの型について・基本構文(関数・ループ・Enum・構造体)を学ぶ
ひとこと: 変数の宣言の仕方からでも、Rustのコンセプトが強く感じられる。
MasaHeroMasaHero

2021/12/09

学習資料

  • 基礎から学ぶ組み込みRust
  • プログラミング言語 Rust 公式ガイド
  • SoftwareDesign 2020年6月号

学んだキーワード

  • シャドーイング
  • 束縛(binding)
    • 多言語でいう代入
  • 副作用
    • 状態(変数の値)を変更されること
  • 所有権
  • コピーセマンティクス
  • ムーブセマンティクス
  • スカラー型
    • i32,i8,u8,bool,などの配列型でない変数
  • 参照

関数ついて

定義方法

  • 引数・戻り値は型指定が必要
  • 戻り値はセミコロンをつけない
  • returnをつけるのはエラー処理などの早期リターンに使う
fn add(x:i16,y:i16) -> i32 {
    // 戻り値は";"をつけない
    x + y
}

所有権

コピーセマンティクス

別の変数への拘束によって、所有権と値をコピーする

ムーブセマンティクス

所有権の移動

コンパイルエラーするコード

fn main() {
    // String型をバインドする
    let s = String::from("hello");

    // sをstr_sにMoveする(所有権の移動)
    // コピーではないことに注意する
    let str_s = s;

    // 問題なく出力される
    println!("{}", str_s);

    // 既に移動され、中身がなくなった変数をアクセス(借用)しようとしコンパイルエラーとなる
    println!("{}", s);
}

これならコンパイルできる

fn main() {
    // String型をバインドする
    let s = String::from("hello");

    // sをstr_sにCopyする
    let str_s = s.clone();

    // 問題なく出力される
    println!("{}", str_s);
    println!("{}", s);
}
MasaHeroMasaHero

2021/12/11

struct

// デバッグ用情報出力機能を設定する
#[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}

fn main() {
    // 構造体Rectangleのinstance作成
    let rect1 = Rectangle {
        width: 30,
        height: 50,
    };

    println!(
        "The area of the rectangle is {} square pixels.",
        area(&rect1)
    );

    // 表示内容
    // rect1 is Rectangle { width: 30, height: 50 }
    println!("rect1 is {:?}", rect1);

    // 表示内容
    // rect1 is Rectangle {
    //     width: 30,
    //     height: 50,
    // }
    println!("rect1 is {:#?}", rect1);
}

メソッド記法

struct Rectangle {
    width: u32,
    height: u32,
}

impl Rectangle {
    fn area(&self) -> u32 {
        self.width * self.height
    }
}

enum


enum A {
    
}

Option<T>

enum Option<T> {
    Some<T>,
    None,
}

matchフロー制御演算子

パターンは、リテラル値、変数名、ワイルドカードやその他多数のもので構成することができます。

switch文のような使い方

使用例


#![allow(unused)]
fn main() {
enum Coin {
    Penny,
    Nickel,
    Dime,
    Quarter,
}

fn value_in_cents(coin: Coin) -> u32 {
    match coin {
        Coin::Penny => 1,
        Coin::Nickel => 5,
        Coin::Dime => 10,
        Coin::Quarter => 25,
    }
}
}


モジュール

mod

pub

use

MasaHeroMasaHero

2021 /12/20

参考図書

実践Rust入門 3章

学習内容

  • ジェネリクス
  • Rustは静的型付き言語「コンパイル時に全ての変数・戻り値・引数に型がついて、妥当性の検査を行う」
    • PythonやRubyなどは動的型付き言語
      • 静的型付き言語のコンパイル時に判明する型に関するエラーが、実行時に判明することもある
  • クロージャ
  • テスト
  • 並列処理
    • Rayon
      • Sync

2021/12/24

Rustの型

分類

  • Primitive(組み込み済み)

    • Scalar(単純な型)
      • ユニット
      • Bool
      • 固定精度の整数型
      • 固定精度の浮動小数点型
      • 文字
      • 参照
      • 生ポインタ
      • 関数ポインタ
  • User Define(ユーザ定義)

  • Compound(複合型)

    • Primitive
      • tuple
      • Vector
      • array
      • str
      • slice
    • UserDefine
    • Primitive ⁺ UserDefine

整数型

型推定が凄い便利(小並感)

”をつけると可読性があがる。束縛時””は無視される(浮動小数点型も同様)

use std; //    std::any::type_name()を使うため

fn ret_typename<T>(_: T) -> &'static str {
    // &'static は ライフタイムの定義(staticが最大のライフタイム)
    // println!("{}", );
    std::any::type_name::<T>()
}
fn main() {
    println!("Hello World!");
    let n1 = 10_000;
    let n2 = 0u8;
    let n3 = -100_isize;

    println!("{}: {}", ret_typename(n1), n1);
    println!("{}: {}", ret_typename(n2), n2);
    println!("{}: {}", ret_typename(n3), n3);
}

stringify!

変数名を返すマクロ

https://doc.rust-lang.org/std/macro.stringify.html

与えられた引数の名前をそのまま返すマクロ。

将来処理が変わるかもしれないから、依存し過ぎないようにすること。

macro_rules! stringify {
    ($($t : tt) *) => { ... };
}
let one_plus_one = stringify!(1 + 1);
assert_eq!(one_plus_one, "1 + 1");

Primitive型を学ぶために処理の共通化

変数名と型名と値を少ない文字数で表示するためにマクロとstdライブラリを使い、関数化

main.rs

main.rs
use std; //    std::any::type_name()を使うため

fn display_var_info<T: std::fmt::Display>(varname: &str, arg: T) {
    println!("{}[{}] = {}", varname, std::any::type_name::<T>(), arg);
}

fn main() {
    println!("Hello World!");
    let n1 = 10_000;
    let n2 = 0u8;
    let n3 = -100_isize;

    display_var_info(stringify!(n1), n1);
    display_var_info(stringify!(n2), n2);
    display_var_info(stringify!(n3), n3);

    let x4 = 10;
    let x5 = 250;
    display_var_info(stringify!(x4), x4);
    display_var_info(stringify!(x5), x5);
    let x6 = x4 + n2;
    display_var_info(stringify!(x6), x6);
}
MasaHeroMasaHero

2021/1/4

参考図書:Rust ツアー

Result型

enum Result<T, E> {
    Ok(T),
    Err(E),
}

Error処理

fn do_something_that_might_fail(i: i32) -> Result<f32, String> {
    if i == 42 {
        Ok(13.0)
    } else {
        Err(String::from("正しい値ではありません"))
    }
}

// 等価
do_something_that_might_fail()?

// 等価
match do_something_that_might_fail() {
    Ok(v) => v,
    Err(e) => return Err(e),
}

Error処理

やっつけな Option/Result 処理

Option と Result の両方には unwrap と呼ばれる関数があり、手っ取り早く値を取得するのには便利である。

unwrap は以下のことを行う。

  1. Option/Result 内の値を取得します。
  2. 列挙型が None/Err の場合、panic!します。

以下の2つのコードは等価です。

my_option.unwrap()
match my_option {
    Some(v) => v,
    None => panic!("Rust によって生成されたエラーメッセージ!"),
}

同様に:

my_result.unwrap()
match my_result {
    Ok(v) => v,
    Err(e) => panic!("Rust によって生成されたエラーメッセージ!"),
}
MasaHeroMasaHero

今日の目標    : Rustの復習(ジェネリクス・構造体・Optionを中心)
今日やる教材   : Rust入門 (https://zenn.dev/mebiusbox/books/22d4c1ed9b0003/viewer/6d5875)

借用チェック

同一オブジェクトに対する参照について、下記の制限がある。

  • 不変参照&は複数個作っても良い
  • 不変参照&と可変参照mut&は同時に存在できない
  • 可変参照mut&は複数個作れない

借用チェック前に複数の可変参照ができてもよいが、借用チェック時には上記の制約を守る必要がある。

可変参照が作成されるときは、それまでの可変・不変参照が全て無効になる。

fn run2() {
    let mut a = 10;
    let a_ref1 = &a;
    let a_mut_ref1 = &mut a;   // mutable reference
    let a_mut_ref2 = &mut a;  // mutable reference ここで a_mut_ref1が無効になる。(参照するとエラーになる。)
    *a_mut_ref2 = 20;
      println!("{}", a);
}
MasaHeroMasaHero

トレイト

例文

// トレイト宣言
  pub trait Geometry {
      fn area(&self) -> f64;
      fn name(&self) -> &str {
          return "Geometry";
      }
  }

  struct Rectangle {
      width: u32,
      height: u32,
  }

// トレイト実装
  impl Geometry for Rectangle {
      fn area(&self) -> f64 {
          self.width as f64 * self.height as f64
      }
      fn name(&self) -> &str {
          "Rectangle"
      }
  }

  struct Triangle {
      bottom: u32,
      height: u32,
  }

  impl Geometry for Triangle {
      fn area(&self) -> f64 {
          self.bottom as f64 * self.height as f64 * 0.5
      }
      fn name(&self) -> &str {
          "Triangle"
      }
  }

  fn main() {
      let a = Rectangle {
          width: 10,
          height: 20,
      };

      let b = Triangle {
          bottom: 20,
          height: 5,
      };
      println!("{} area={}", a.name(), a.area());
      println!("{} area={}", b.name(), b.area());
  }

Trait継承

pub trait Trait_A{
    ...
}

pub trait Trait_B : Trait_A{
    ...
} 

struct Struct_A {

}

impl Trait_B for Struct_A{
}

MasaHeroMasaHero

今日の目標    : Rustの復習(ジェネリクス・構造体・Optionを中心)
今日やる教材   : Rust入門 (https://zenn.dev/mebiusbox/books/22d4c1ed9b0003/viewer/6d5875)

参照カウンタ

  • 一つのオブジェクトに対して、複数の変数から所有権を持つことができる。
  • use std::rc::Rc;で使う
  • `Rc::clone(&<objectA>)で仮の所有権を生成する
  • 全ての変数から所有権を破棄したときにオブジェクトが破棄される
MasaHeroMasaHero

Rc::strong_count(&object)で参照されている回数(仮所有権の数)が取得できる。

    let a = Rc::new(10);        // shared reference to immutable object
    dbg!(Rc::strong_count(&a)); // Rc::strong_count(&a) = 1
    let b = Rc::clone(&a);      // cloned shared reference
    dbg!(Rc::strong_count(&a)); // Rc::strong_count(&a) = 2
    dbg!(Rc::strong_count(&b)); // Rc::strong_count(&b) = 2

dbg!でデバッグが便利になる。

[src/main.rs:5] Rc::strong_count(&a) = 1
[src/main.rs:8] Rc::strong_count(&a) = 2
[src/main.rs:9] Rc::strong_count(&b) = 2

Rc::clone()は不変参照だが、Cell型とRefCell型は可変参照が可能。
Cell型とRefCell型の違いは下記のURL

https://qiita.com/mosh/items/c7d20811df218bb3188e

MasaHeroMasaHero

学習進捗

Chapter 01 はじめに
Chapter 02 基本
Chapter 03 コピートレイト
Chapter 04 データ型
Chapter 05 関数
Chapter 06 制御構文
Chapter 07 構造体
Chapter 08 列挙型
Chapter 09 ジェネリクス
Chapter 10 Option
Chapter 11 パターンマッチング
Chapter 12 エラー処理
Chapter 13 トレイト
Chapter 14 RAII
Chapter 15 内部可変性
Chapter 16 クロージャ
Chapter 17 ライフタイム
Chapter 18 並列処理
Chapter 19 所有権のまとめ
Chapter 20 マクロ
Chapter 21 イテレータ
Chapter 22 コレクション
Chapter 23 Cargo
Chapter 24 モジュール
Chapter 25 ユニットテスト
Chapter 26 Tips
Chapter 27 さいごに

MasaHeroMasaHero

並列処理

use std::thread;
let handle = thread::spawn(|| {
    // thread code
});
handle.join().unwrap();
  • スレッドコード内に所有権を渡す場合はmoveを使うこと
let handle = thread::spawn(move || {
        println!("Here's a vector: {:?}", v);
    });
  • Mutex は、常に1つのスレッドがリソース操作できる
  • RwLockは、不変参照なら複数のスレッドで参照できる
  • Arcは、Rcのマルチスレッドセーフ版
MasaHeroMasaHero

RAII (Resource Acquisition Is Initialization)

リソースの確保は初期化である。(直訳)

  • オブジェクト生成時にリソース確保
  • オブジェクト破棄時にリソース開放