4
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Rustの用語集【備忘録】

Last updated at Posted at 2020-01-01

TL; DR

昨日初めてRustを触ってみたメモと備忘録です。
まだ序盤なので全然少ないのですが、随時追記していこうと思います。
※ 2020/01/05 追記

コマンド

インストールした際に追加されるコマンド群
リリースチャンネルとしてstable/beta/nightlyがあり、各チャンネルは以下のようになっている。

  • stable : 安定板
  • beta : nightlyのテストが完了したもの
  • nightly : プレビュー版

基本的にはstableを使うことをおすすめする。

rustc

ソース : https://github.com/rust-lang/rust/tree/master/src/rustc
Rust言語のコンパイラで、ソースコードからライブラリやバイナリファイルを生成する。rustc hoge.rsでビルドが実施できる。
cargo buildも同様なので、こちらが使われることが多い。
単体でサクッと試したいときはいいかも。
https://doc.rust-lang.org/rustc/what-is-rustc.html

rustup

ソース : https://github.com/rust-lang/rustup
RustのToolchainインストーラーでデフォルトの場合rustc/cargo/rustupをインストールする。rust updateでtoolchainやrustupのアップデートができる。
リリースチャンネルを指定して、インストールやビルドが実行できる。

# nightlyからインストール
$ rustup install nightly
# betaでビルド
$ rustup run beta cargo run

cargo

パッケージ管理およびビルド実行ツールとして利用できる。上述したrustcはほぼほぼこっちに駆逐されている気がする。新規プロジェクトの作成やテスト、お片づけなどもしてくれる優れもの。

主要コマンド

  • cargo new : 新規プロジェクトを作成する
  • cargo build : ソースコードをビルド
  • cargo run : ビルドファイルを実行する
  • cargo test : テストを実行する
tests/lib.rs
fn main(x: i32) -> i32 {
    x * 4
}

# [cfg(test)]  //テストであることを宣言
mod tests {   //テストモジュールとしてグループ化
    use super::*;

    #[test]   //テストパターン
    fn succeed_test() {
        assert_eq!(8, main(2));
    }

    #[test]
    fn failure_test() {
        assert_eq!(4, main(2));
    }
}
result
$ cargo test
   Compiling cargo_test v0.1.0 (/Users/user/into-rust/cargo_test)
    Finished test [unoptimized + debuginfo] target(s) in 0.51s
     Running target/debug/deps/cargo_test-f3e16b982542493f

running 2 tests
test tests::succeed_test ... ok      //成功パターンが成功していることがわかる
test tests::failure_test ... FAILED  //失敗パターンが失敗していることがわかる

failures:

---- tests::failure_test stdout ----
thread 'tests::failure_test' panicked at 'assertion failed: `(left == right)`
  left: `4`,
 right: `8`', src/main.rs:16:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.


failures:
    tests::failure_test

test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out

error: test failed, to rerun pass '--bin cargo_test'
  • cargo clippy : 怪しげなやつを教えてくれる
$ cargo clippy
warning: redundant field names in struct initialization
  --> src/main.rs:15:13
   |
15 |             left: left,
   |             ^^^^^^^^^^ help: replace it with: `left`
   |
   = note: `#[warn(clippy::redundant_field_names)]` on by default
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names

warning: redundant field names in struct initialization
  --> src/main.rs:16:13
   |
16 |             right: right,
   |             ^^^^^^^^^^^^ help: replace it with: `right`
   |
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names

    Finished dev [unoptimized + debuginfo] target(s) in 0.01s
  • cargo clean : cargo run等で生成されたtargetを消してくれる。
cargo clean
$ ls -l
total 16
drwxr-xr-x  5 user  staff  160  1  3 10:40 .
drwxr-xr-x  3 user  staff   96 12 30 12:41 ..
-rw-r--r--  1 user  staff  142 12 30 12:41 Cargo.lock
-rw-r--r--  1 user  staff  233 12 30 12:41 Cargo.toml
drwxr-xr-x  3 user  staff   96 12 30 11:08 src
$ 
$ cargo run
   Compiling helloworld v0.1.0 (/Users/user/Documents/GitHub/into-rust/cargo_new/helloworld)
    Finished dev [unoptimized + debuginfo] target(s) in 0.34s
     Running `target/debug/helloworld`                  # バイナリファイルを含むtagetが生成される
hello, world
$ 
$ ls -l
total 16
drwxr-xr-x  6 user  staff  192  1  3 10:40 .
drwxr-xr-x  3 user  staff   96 12 30 12:41 ..
-rw-r--r--  1 user  staff  142 12 30 12:41 Cargo.lock
-rw-r--r--  1 user  staff  233 12 30 12:41 Cargo.toml
drwxr-xr-x  3 user  staff   96 12 30 11:08 src
drwxr-xr-x  4 user  staff  128  1  3 10:40 target       # Githubとかにあげるときには不要
$ cargo clean
$ 
$ ls -l
total 16
drwxr-xr-x  5 user  staff  160  1  3 10:40 .
drwxr-xr-x  3 user  staff   96 12 30 12:41 ..
-rw-r--r--  1 user  staff  142 12 30 12:41 Cargo.lock
-rw-r--r--  1 user  staff  233 12 30 12:41 Cargo.toml
drwxr-xr-x  3 user  staff   96 12 30 11:08 src         # targetごと削除されたのがわかる

rustfmt

任意の設定に応じてファイルを整形してくれる。
設定ファイルの置き場は下記を参照
※macは$HOME/Library/Preferences
https://docs.rs/dirs/1.0.4/dirs/fn.config_dir.html
上記配下にrustfmt/rustfmt.tomlをおけばOK
設定項目については以下を参照されたし。
https://github.com/rust-lang/rustfmt/blob/master/Configurations.md

sample rustfmt.toml
tab_spaces = 2
error_on_line_overflow = true
error_on_unformatted = true
version = "Two"

Syntax

*.rsファイルにおける記法および構成要素

変数束縛

Rustの場合key:valueが1:1になる。
また変数はデフォルトでイミュータブルなものとして扱われる。
型を明示的に書く場合はkeyのあとにコロンで指定する。
※型推論が効くので不要な場合も多い

型指定
fn main() {
    let x: i32 = 5;
}

変数に再代入などをしたい場合はmutを用いて可変なものである、ということを明示してあげる必要がある。

再代入
fn main() {
    let mut x: i32 = 5;
    let x = 10;
}

変数束縛はブロック内でしか有効でないインスタンス変数のような特性を持つ。
また同じ名前の変数束縛がある場合、後続の方に上書きされる。この際、違う型へ束縛することも可能。これをシャドーイングと言う。

shadowing
fn main() {
    let mut x: i32 = 1;
    println!("{}", x);

    x = 7;
    let x = x;
    
    let y = 4;
    println!("{}", y);

    let y = "variable change intenger to string";

    println!("{}", x);
    println!("{}", y);
}
=================result=================

$ cargo run
   Compiling helloworld v0.1.0 (/Users/user/into-rust/cargo_new/helloworld)
    Finished dev [unoptimized + debuginfo] target(s) in 0.28s
     Running `target/debug/helloworld`
1
4
7
variable change intenger to string

プリミティブ型

Rustには様々な型があり、それらが言語に組み込まれている。
Primitive Types

  • boolean型
let x: bool = true;  // or false;

true/falseの型で主にif条件文で用いられる。

  • char型
let x = 'x';
let stars = '✨';

シングルクオートで囲われたユニコードのスカラ値を表す型で、Rustの場合4バイトであることを意味する。

  • intenger型
category class
符号あり i8/i16/i32/i64
符号なし u8/u16/u32/u64
可変長型 isize/usize
浮動小数点 f32/f64
  • array型
array
// Basic array
let a = [1, 2, 3];
let mut m = [1, 2, 3];
// [T; N] array
let a = [0; 20];  // 変数の各要素を0で初期化
  • slice構文

スライスの要素として&[]がある。
&はスライスを参照することを表す。
[]は範囲を持ち、スライスの長さを定義する。

slicing
let a = [0, 1, 2, 3, 4];
let complete = &a[..]; // aに含まれる全ての要素を持つスライス
let middle = &a[1..4]; // 1、2、3のみを要素に持つaのスライス
  • str型

文字列には2種類あり、プリミティブ型のstrと標準ライブラリのStringが存在する。
&strの場合、文字列スライスとして扱う。

&str
fn main() {
    let x = "Hello ".to_string();
    let y = "world!".to_string();

    println!("{}", x + &y);
}
====================result====================
$ cargo run
   Compiling helloworld v0.1.0 (/Users/user/into-rust/cargo_new/helloworld)
    Finished dev [unoptimized + debuginfo] target(s) in 0.46s
     Running `target/debug/helloworld`
Hello world!
  • tuple型
    タプル型は固定サイズの順序かつリストとなるもの。
    下記例のようにインデックス構文でアクセスすることも可能だが、その場合[]ではなく.でアクセスする
tuple
fn main() {
    let x = (1, "hello");
    // 型注釈も可能
    let y: (i32, &str) = (2, "world");
    
    println!("{}", x.0);
    println!("{}", x.1);
    println!("{}", y.0);
    println!("{}", y.1);
}

=======================result=======================
$ cargo run
   Compiling helloworld v0.1.0 (/Users/user/into-rust/cargo_new/helloworld)
    Finished dev [unoptimized + debuginfo] target(s) in 0.26s
     Running `target/debug/helloworld`
1
hello
2
world

型が一致していれば他のタプルに入れ込むことも可能

fn main() {
    let mut x: (i32, &str) = (1, "hello");
    let y: (i32, &str) = (2, "3");

    x = y;
    
    println!("{}", x.0);
    println!("{}", x.1);
}
---------------------result--------------------
$ cargo run
   Compiling helloworld v0.1.0 (/Users/user/into-rust/cargo_new/helloworld)

    Finished dev [unoptimized + debuginfo] target(s) in 0.27s
     Running `target/debug/helloworld`
2
3

=====================型不一致====================

fn main() {
    let mut x: (i32, &str) = (1, "hello");
    let y: (i32, i32) = (2, 3);

    x = y;
    
    println!("{}", x.0);
    println!("{}", x.1);
}
---------------------result--------------------
$ cargo run
   Compiling helloworld v0.1.0 (/Users/user/into-rust/cargo_new/helloworld)
error[E0308]: mismatched types
 --> src/main.rs:5:9
  |
5 |     x = y;
  |         ^ expected &str, found i32
  |
  = note: expected type `(i32, &str)`
             found type `(i32, i32)`

error: aborting due to previous error

またタプルの各要素を変数として定義してアクセスできる分配束縛というものもある。

let (x, y, z) = (1, 2, 3);

println!("x is {}", x);
  • 関数

関数も型注釈を入れることができる

function
fn foo(x: i32) -> i32 { x }

let x: fn(i32) -> i32 = foo;

vec(ベクタ)

ベクタは動的な配列であり拡張可能な配列です。標準ライブラリで提供されている。
インデックス構文で要素にアクセスが可能だが、その際インデックスはusize型でなければいけない。

vec!
fn main() {
    let x = vec![1, 2, 3, 4, 5];
    let y = vec![1, 2, 3, 4, 5];

    println!("{}", x[0]);
    println!("{}", y[3]);
    println!("{}", x[0] + y[3]);
}
================result================
$ cargo run
   Compiling helloworld v0.1.0 (/Users/user/into-rust/cargo_new/helloworld)
    Finished dev [unoptimized + debuginfo] target(s) in 0.42s
     Running `target/debug/helloworld`
1
4
5
usize
fn main() {
    let x = vec![1, 2, 3, 4, 5];
    let y = vec![1, 2, 3, 4, 5];
    
    let a: usize = 0;
    let b: i32 = 3;

    println!("{}", x[a]);
    println!("{}", y[b]);
    println!("{}", x[a] + y[b]);
}
================result================
$ cargo run
   Compiling helloworld v0.1.0 (/Users/user/into-rust/cargo_new/helloworld)
error[E0277]: the type `[{integer}]` cannot be indexed by `i32`
 --> src/main.rs:9:20
  |
9 |     println!("{}", y[b]);
  |                    ^^^^ slice indices are of type `usize` or ranges of `usize`
  |
  = help: the trait `std::slice::SliceIndex<[{integer}]>` is not implemented for `i32`
  = note: required because of the requirements on the impl of `std::ops::Index<i32>` for `std::vec::Vec<{integer}>`

forを用いての繰り返しも可能

for
fn main() {
    let mut x = vec![1, 2, 3, 4, 5];

    for i in &x {
        println!("A reference to {}", i);
    }
    
    for i in &mut x {
        println!("A mutable reference to {}", i);
    }
    
    for i in x {
        println!("Take ownership of the vector and its element {}", i);
    }
}

impl(メソッド呼び出し)

impl構文を利用してメソッドを定義することができる。
その際第一引数に関してself/&self/&mut selfと定義することが可能だが、基本的に借用を利用しイミュータブルな参照を渡すべきなので&selfを置くのが基本

impl
impl Circle {
    fn area(&self) -> f64 {
        std::f64::consts::PI * (self.radius * self.radius)
    }
}

crate(クレート)

クレートとは他の言語におけるライブラリパッケージにあたるもの。
参照させるには色々なやり方がある。

tree
$ tree
.
├── Cargo.lock
├── Cargo.toml
└── src
    ├── chinese
    │   ├── evening.rs
    │   ├── mod.rs
    │   └── moring.rs
    ├── lib.rs
    ├── main.rs
    └── spanish
        ├── evening.rs
        ├── mod.rs
        └── moring.rs
add pub everything
// main.rs
extern crate crate_demo;

fn main() {
    println!("おはよう in Chinese: {}", crate_demo::chinese::morning::morning());
    println!("こんばんは in Chinese: {}", crate_demo::chinese::evening::evening());
    println!("おはよう in Spanish: {}", crate_demo::spanish::morning::morning());
    println!("こんばんは in Spanish: {}", crate_demo::spanish::evening::evening());
}

// lib.rs
pub mod chinese;
pub mod spanish;

//*/mod.rs
pub mod morning;
pub mod evening;

//*/morning.rs
pub fn morning() -> String {
    "hogehoge".to_string()
}

//*/evening.rs
pub fn evening() -> String {
    "fugafuga".to_string()
}

もしくはuseselfを利用して

use self pattern
// main.rs
extern crate crate_demo;

use crate_demo::chinese::{morning, evening};  //複数クレートを指定してインポート
use crate_demo::spanish;                      //モジュール階層を指定してインポート

fn main() {
    println!("おはよう in Chinese: {}", morning::morning());
    println!("こんばんは in Chinese: {}", evening::evening());

    println!("おはよう in Spanish: {}", spanish::morning());
    println!("こんばんは in Spanish: {}", spanish::evening());
}

// lib.rs
pub mod chinese;
pub mod spanish;

//*/mod.rs
pub use self::morning::morning;  //各クレートのスコープをモジュールの親階層にselfで設定している
pub use self::evening::evening;

mod morning;
mod evening;

//*/morning.rs
pub fn morning() -> String {
    "hogehoge".to_string()
}

//*/evening.rs
pub fn evening() -> String {
    "fugafuga".to_string()
}

useを宣言した場合、ルートディレクトリからの絶対パスとして認識される。
selfは現在地からの相対パスとして認識される。

mod(モジュール)

下記のようなディレクトリ構成の場合、hoge.rsを指定してビルドする必要はなくrustc main.rsを実行するだけで意図した結果が得られる。
モジュール名にはlower_snake_caseを利用する。
モジュールはデフォルトでプライベートなため、他の関数などから参照するにはpubを先頭につける必要がある。配下のサブモジュールや関数は::で参照ができる。

$ tree
.
├── hoge.rs
├── main
└── main.rs
main.rs
mod hoge;

fn main() {
    hoge::hello();
}
hoge.rs
pub fn hello() {
    println!("Hello, world!");
}
result
$ ./main
Hello, world!

if文

shellとあんまり変わんないけど式で書けるので、下記みたいなものでも成り立つ。

if syntax
let x = 5;

let y = if x == 5 {
    10
} else {
    15
};

// 上記のように無駄に改行しない
let x = 5;

let y = if x == 5 { 10 } else { 15 };

繰り返し

Rustの繰り返しには下記の記法がある。

  • loop
  • while
  • for
loop
// 無限ループする
loop {
    println!("Loop forever!");
}

// break;で終わらすことができる
let mut x = 5;

loop {
    x += x - 3;

    println!("{}", x);

    if x % 5 == 0 { break; }
}

// continue;で次の処理を再開することができる
for x in 0..10 {
    if x % 2 == 0 { continue; }

    println!("{}", x);
}
while
// 5で割り切れるまで繰り返す
let mut x = 5;
let mut done = false;

while !done {
    x += x - 3;

    println!("{}", x);

    if x % 5 == 0 {
        done = true;
    }
}
for
// 0から9まで出力する
for x in 0..10 {
    println!("{}", x);
}

// 範囲指定して5から9まで出力する
for x in 5..10 {
    println!("{}", x);
}

ループラベル
繰り返し対象にラベルをつけて実行対象を制御することができる。

'outer: for x in 0..10 {
    'inner: for y in 0..10 {
        if x % 2 == 0 { continue 'outer; } // x のループを継続
        if y % 2 == 0 { continue 'inner; } // y のループを継続
        println!("x: {}, y: {}", x, y);
    }
}

構造体(struct)

Rustにはクラスがなくstructimpltraitで使い分ける。
structはパッと見オブジェクトっぽく見えるが、文字タイプの指定などを(構造化)しているだけで実際のデータ値を持っているわけではない。structの命名にはキャメルケースを使う。origin.xのようなドット表記で値にアクセスすることが可能。

struct
struct Point {
    x: i32,
    y: i32,
}

fn main() {
    let origin = Point { x: 3, y: 2 };

    println!("The origin is at ({}, {})", origin.x, origin.y);
}

structもやっぱりイミュータブルだけど、ミュータブルにもできる。

mutable
// OK例
fn main() {
    let mut point = Point { x: 0, y: 0 };

    point.x = 5;

    println!("The point is at ({}, {})", point.x, point.y);
}

// NG例 言語レベルでフィールドの変更は許可していない。
struct Point {
    mut x: i32,
    y: i32,
}

アップデート構文
.を利用して不要な変更を省略(デフォルトを利用)することができる。

update
struct Point3d {
    x: i32,
    y: i32,
    z: i32,
}

let mut point = Point3d { x: 0, y: 0, z: 0 };
point = Point3d { y: 1, .. point };  // 変更が必要ない部分は..で省略している

let origin = Point3d { x: 0, y: 0, z: 0 };
let point = Point3d { z: 1, x: 2, .. origin };

タプル構造体
タプルを利用した構造体を指定することもできるが、フィールドが単一(newtypeパターン)か空(unit-like)の場合以外あまりオススメしない。

taple
struct Color(i32, i32, i32);
struct Point(i32, i32, i32);

let black = Color(0, 0, 0);
let origin = Point(0, 0, 0);

// newtypeパターン
struct Inches(i32);

let length = Inches(10);

let Inches(integer_length) = length;
println!("length is {} inches", integer_length);

// unit-likeパターン
struct Electron;

let x = Electron;Run

列列挙(enum)

enum構文は、struct構文と似ているがstructとは違い色々なvariantを持つことが可能

  • データを持たないヴァリアント
  • 名前付きデータを持つヴァリアント
  • 名前なしデータを持つヴァリアント

ヴァリアントの名前はenum自体の名前によってスコープ化されているため、各ヴァリアントの名前を使うために::構文を使用する。

enum
enum Message {
    Quit,
    ChangeColor(i32, i32, i32),
    Move { x: i32, y: i32 },
    Write(String),
}

let x: Message = Message::Move { x: 3, y: 4 };               //Message列列挙からMoveを呼び出している

enum BoardGameTurn {
    Move { squares: i32 },
    Pass,
}

let y: BoardGameTurn = BoardGameTurn::Move { squares: 1 };  //BoardGameTurn列列挙からMoveを呼び出している

// 関数っぽくも使える
let m = Message::Write("Hello, world".to_string());

マッチ(match)

パターンが多い場合if/elseだと冗長になりがちなものをmatch構文で列挙することで条件分岐させることができる。マッチしない場合はpanicになるのでエラーハンドリングを意識した書き方が必要になる。
またmatchは式でもあるため、式形式で書くことで型変換を実施することもできる。

match
let x = 5;              // 整数型

let number = match x {  // match x { で始まると、_で型不一致によるビルドエラーが起こる
    1 => "one",
    2 => "two",
    3 => "three",
    4 => "four",
    5 => "five",
    _ => "something else",
};

enumへのマッチ
列列挙で記載された条件に対してもmatch構文で処理を実施することが可能

matching enum
enum Message {
    Quit,
    ChangeColor(i32, i32, i32),
    Move { x: i32, y: i32 },
    Write(String),
}

fn quit() { /* ... */ }
fn change_color(r: i32, g: i32, b: i32) { /* ... */ }
fn move_cursor(x: i32, y: i32) { /* ... */ }

fn process_message(msg: Message) {
    match msg {
        Message::Quit => quit(),
        Message::ChangeColor(r, g, b) => change_color(r, g, b),
        Message::Move { x: x, y: y } => move_cursor(x, y),
        Message::Write(s) => println!("{}", s),
    };
}

その際にマッチの特性上、コンパイラが網羅的に列列挙のvariantに対するマッチの条件処理が記載されているかを確認するため一つでも処理が記載されていないもの or **_を用いた例外処理(matchしないもの)**のいずれかを実装していない場合panicを起こすので注意が必要

パターン

変数束縛やマッチで利用されスコープの中で有効なため、シャドーイングされて処理が実行される。
またパイプを利用して複数条件を指定することができる。

pattern
let x = 1;

match x {
    1 | 2 => println!("one or two"),
    3 => println!("three"),
    _ => println!("anything"),
}

println!("{}", x)

分配束縛
タプルや列挙型、構造体などの複合データをパターン内で要素ごとに分解することができる。

destructing
struct Point {
    x: i32,
    y: i32,
}

let origin = Point { x: 0, y: 0 };

match origin {
    Point { x, y } => println!("({},{})", x, y),
}

// スコープ内で値に別名をつけることが可能
struct Point {
    x: i32,
    y: i32,
}

let origin = Point { x: 0, y: 0 };

match origin {
    Point { x: x1, y: y1 } => println!("({},{})", x1, y1),
}

// 一部だけ取り出して処理することが可能
struct Point {
    x: i32,
    y: i32,
}

let origin = Point { x: 0, y: 0 };

match origin {
    Point { x, .. } => println!("x is {}", x),
}

束縛の無視
_..を利用して例外処理や一部分のみを束縛することができる。

exception
// アンダースコアパターン
match some_value {
    Ok(value) => println!("got a value: {}", value),  // OKのvariant値をvalueで束縛している
    Err(_) => println!("an error occurred"),          // Errのvariant値を_を利用して例外を処理している
}

// ドットパターン
enum OptionalTuple {
    Value(i32, i32, i32),
}

let x = OptionalTuple::Value(5, -2, 3);

match x {
    OptionalTuple::Value(..) => println!("Got a tuple!")
}

マッチの色々な書き方

  • refとref mut

refを利用して参照を取得することが可能

ref
let x = 5;

match x {
    ref r => println!("Got a reference to {}", r),
}

// ミュータブルな参照(mut T)をする場合場合
let mut y = 5:

match y {
    ref mut mr => println!("Got a mutable reference to {}", mr),
}
  • 範囲

文字列で使われることが多いパターンで...を利用して範囲を示す

range
let x = '💅';

match x {
    'a' ... 'j' => println!("early letter"),
    'k' ... 'z' => println!("late letter"),
    _ => println!("something else"),
}
  • 束縛

@で値に名前を束縛することができる。

fix name
let x = 1;

match x {
    e @ 1 ... 5 => println!("got a range element {}", e),  // 1から5をeとして束縛
    _ => println!("anything"),
}
complexity matching
# [derive(Debug)]
struct Person {
    name: Option<String>,
}

let name = "Steve".to_string();
let mut x: Option<Person> = Some(Person { name: Some(name) });
match x {
    Some(Person { name: ref a @ Some(_), .. }) => println!("{:?}", a),  // nameの値をaとして束縛し、参照した値をつっこむ
    _ => {}
}

パイプ(|)を利用した複数パターンを扱う場合は名前は一致しなければいけない

conbination
let x = 5;

// NG例
match x {
    a @ 1 ... 5 | b @ 8 ... 10 => println!("got a range element {}", *),  // 1..5と8..10の名前が一致していない
    _ => println!("anything"),
}

// OK例
match x {
    e @ 1 ... 5 | e @ 8 ... 10 => println!("got a range element {}", e),
    _ => println!("anything"),
}
  • ガード

ifを利用してマッチガードを実装できる

if
enum OptionalInt {
    Value(i32),
    Missing,
}

fn main() {
    let x = OptionalInt::Value(7);

    match x {
        OptionalInt::Value(i) if i > 5 => println!("Got an int bigger than five!"),
        OptionalInt::Value(..) => println!("Got an int!"),  // 上記のif文を満たさないためそれ以外(..)として処理される
        OptionalInt::Missing => println!("No such luck."),
    }
}

上記の例の場合.._でも問題ない。

複式パターンで if を使うと、 if は | の両側に適用されます:

multi if
let x = 4;
let y = false;

match x {
    4 | 5 if y => println!("yes"),  // xは4ではあるが5ではないため例外(_)として処理される
    _ => println!("no"),
}

上記のとおり|を用いる場合、(4 | 5) if y => ...として扱われ4 | (5 if y) => ...としては扱われない。

文字列

Rustにおける文字列は動的なサイズを持つデータ構造であり、nullバイトを保持することもできる。
文字列はUTF-8のバイトストリームとしてエンコードされたユニコードのスカラ値のシーケンスである。
Rustの文字列は2種類あり、&str(文字列スライス)とString(ヒープアロケートされる文字列)が存在する。

  • &str(文字列スライス)

&strはUTF-8のバイトシーケンスへの参照のため、固定サイズであり変更不可な文字列となる。
let greeting = "Hello there.";という文字列はlet greeting: &'static str = "Hello there.";と同意であり&'static str型を持つ。
つまりコンパイル時に静的に割り当てられているためプログラム内で一意となるため変更が不可になる。
また文字列リテラルは複数行に渡って記載することができる。

multi line
// 改行と行頭の空白を含む形式
let s = "foo
    bar";

assert_eq!("foo\n        bar", s);Run

// \ を使って空白と改行を削る
let s = "foo\
    bar";

assert_eq!("foobar", s);
  • String

String文字列は伸張可能であり、またUTF-8であることも保証されている。
Stringは文字列スライスをto_stringメソッドで変換することで作成されるが、メモリアロケーションが発生するため必要以上に実施するのは控えるのが好ましい。

let mut s = "Hello".to_string(); // 変数sはStringとして認識される
println!("{}", s);               // to_stringメソッドで変換しないとエラーになる

String は & によって &str に型強制されるが、&strの実装するトレイトを引数として取る関数に対しては自動では変換されないため&*にて明示的に変換が必要になる。

型強制
// Stringの型強制
fn takes_slice(slice: &str) {
    println!("Got: {}", slice);
}

fn main() {
    let s = "Hello".to_string();
    takes_slice(&s);
}

// 明示的な変換
use std::net::TcpStream;

TcpStream::connect("192.168.0.1:3000");  // 引数として&strが必要

let addr_string = "192.168.0.1:3000".to_string();
TcpStream::connect(&*addr_string);      // addr_stringを&strに変換して渡す
  • 文字列におけるインデクシング(非対応)

文字列に対するインデクシングは言語レベルで許可されていない。
理由はUTF-8をエンコードしているため、バイトが固定長でない可能性があるため。
ただしインデクシングに近いような記述は可能になっている。

nearly indexing
fn main() {
    let hachiko = "忠犬ハチ公";
    let dog = hachiko.chars().nth(1).unwrap();; 

    for b in hachiko.as_bytes() {
        print!("{}, ", b);
    }
        
    for c in hachiko.chars() {
        print!("{}, ", c);
    }
    
    println!("{}", dog);
}

========================result========================

229, 191, 160, 231, 138, 172, 227, 131, 143, 227, 131, 129, 229, 133, 172,
, , , , ,

  • スライシング
slicing
// OK例
fn main() {
    let hachiko = "hachiko";
    let name = &hachiko[1..4];

    println!("{}", name);
}

// NG例(漢字の場合バイト数が固定長じゃないため)
fn main() {
    let hachiko = "忠犬ハチ公";
    let dog2 = &hachiko[..1];

    println!("{}", dog2);

=====================result=====================
thread 'main' panicked at 'byte index 1 is not a char boundary; it is inside '忠' (bytes 0..3) of `忠犬ハチ公`'
  • 連結

Stringに対して&strを末尾に連結するできる。
Deref による型強制という&Stringが自動的に&strに型強制される機能があり、String同士を連結する場合下記のように&で繋ぐ必要がある。

linked strings
// String + &str
fn main() {
    let hello = "Hello ".to_string();
    let world = "world!";
    
    let hello_world = hello + world;
}

    println!("{}", hello_world);

// String + String
fn main() {    
    let hello = "Hello ".to_string();
    let world = "world!".to_string();
    
    let hello_world = hello + &world;

    println!("{}", hello_world);
}

所有権/参照と借用/ライフタイム

こちら
内容は下記

所有権

  • 変数束縛のムーブセマンティクス
  • copy型

参照と借用

  • &T型による参照と所有権の借用
  • &mut Tによるミュータブルな参照
  • 借用のルールとスコープ
  • ユースケース的なやつ

ライフタイム

  • ライフタイムa('a)
  • ライフタイムのスコープ
  • グローバルなライフタイム('static)
  • ユースケース的なやつ
4
6
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
4
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?