Rust 基礎構文

変数、基本型、関数、コメント、制御フローは、ほとんどすべてのプログラミング言語に存在するプログラミング概念です。

これらの基本概念はすべての Rust プログラムに存在します。早めに学ぶことで、最速で Rust の使い方を習得できます。

変数

最初に言っておくと、Rust は強い型付けの言語ですが、変数の型を自動的に推論する能力を持っています。このため、弱い型付けの言語と混同しやすいです。

デフォルトでは、Rust の変数は不変です。mut キーワードで可変変数として宣言しない限り、変更できません。

let a = 123;       // 不可变变量
let mut b = 10;  // 可变变量

変数を宣言するには、次のキーワードを使用する必要があります。letキーワード。例:

let a = 123;

JavaScript だけを学んだ開発者はこの文に敏感ですが、C 言語だけを学んだ開発者はこの文をよく理解できません。

この宣言文の後、次の3行のコードはすべて禁止されています:

a = "abc";
a = 4.56; 
a = 456;

1行目のエラーは、a が 123 と宣言された後、a は整数型と確定し、文字列型の値を代入できないことです。

2行目のエラーは、自動変換で数値の精度が失われるため、Rust は精度が失われる自動型変換を許可しないことです。

3行目のエラーは、a が可変変数ではないことです。

最初の2つのエラーは理解しやすいですが、3つ目はどういう意味でしょうか? a は変数ではないのでしょうか?

これは、Rust が高い並行性の安全性のために行った設計に関係します。言語レベルで変数の値を変更できる機会をできるだけ少なくしています。そのため a の値は不変です。しかし、これは a が「変数」(英語の variable) ではないことを意味しません。公式ドキュメントでは a のような変数を「不変変数」と呼んでいます。

プログラムのある部分が値が決して変わらないと仮定して実行されている一方で、コードの別の部分がその値を変更している場合、最初の部分は設計どおりに動作しない可能性があります。このような原因によるエラーは後から見つけるのが困難です。これが Rust がこのメカニズムを設計した理由です。

もちろん、変数を「可変」(mutable)にするには、1つのキーワードが必要です。mutキーワード。

let mut a = 123;
a = 456;

このプログラムは正しいです。

定数と不変変数の違い

不変変数が不変なら、それは定数ではないですか? なぜ変数と呼ぶのでしょうか?

変数と定数にはまだ違いがあります。Rust では、次のプログラムは合法です:

let a = 123;   // 可以编译,但可能有警告,因为该变量没有被使用
let a = 456;

しかし、a が定数の場合は不正になります:

const a: i32 = 123;
let a = 456;

変数の値は「再バインド」できますが、「再バインド」される前に勝手に変更されることはありません。これにより、各「バインド」後の領域でコンパイラがプログラムのロジックを十分に推論できるようになります。Rust には型の自動推論機能がありますが、場合によっては型を明示的に宣言する方が便利です:

let a: u64 = 123;

ここでは a を符号なし64ビット整数型として宣言しています。型を宣言しない場合、a は自動的に符号付き32ビット整数型と推論され、a の値の範囲に大きな影響を与えます。

データ型

Rust は静的型付け言語であり、変数宣言時に型を明示的に指定できますが、通常は型推論に頼ることができます。

基本型:i32 (32ビット符号付き整数), u32 (32ビット符号なし整数), f64 (64ビット浮動小数点数), bool (ブール型), char (文字)

例

let x: i32 = 42;
let y: f64 = 3.14;
let is_true: bool = true;
let letter: char = 'A';

関数

Rust の関数はfnキーワードで定義し、関数の戻り値の型は矢印記号で->指定します。

例

fn add(a: i32, b: i32) -> i32 {
    a + b
}

関数に戻り値がない場合、型はデフォルトで()(つまり空のタプル)になります。

制御フロー

if 式

例

let number = 7;
if number < 5 {
    println!("5未満");
} else {
    println!("5以上");
}

loop ループ:loop は Rust における無限ループで、break を使用してループを抜けることができます。

例

let mut counter = 0;
loop {
    counter += 1;
    if counter == 10 {
        break;
    }
}

while ループ

例

let mut number = 3;
while number != 0 {
    println!("{}!", number);
    number -= 1;
}

for ループ

例

for number in 1..4 {
    println!("{}!", number);
}

所有権 (Ownership)

Rust の所有権は独自のメモリ管理メカニズムであり、中核となる概念には所有権 (ownership)、借用 (borrowing)、参照 (reference) が含まれます。

所有権のルール:

  • Rust のすべての値には所有者が1人(1つ)います。
  • 各値は任意の時点で1つの所有者しか持てません。
  • 所有者がスコープを外れると、値は削除されます。
let s1 = String::from("hello");
let s2 = s1; // s1 的所有权被转移给了 s2
// println!("{}", s1); // 此处编译会报错,因为 s1 已不再拥有该值

借用と参照:借用は、所有権を取得せずにデータを参照することを許可し、&記号で実現します。

fn main() {
    let s = String::from("hello");
    let len = calculate_length(&s);  // 借用
    println!("The length of '{}' is {}.", s, len);
}

fn calculate_length(s: &String) -> usize {
    s.len()
}

構造体 (Structs)

構造体はカスタム型を作成するために使用され、フィールドにはさまざまなデータ型を含めることができます。

例

struct User {
    username: String,
    email: String,
    sign_in_count: u64,
    active: bool,
}

let user1 = User {
    username: String::from("someusername"),
    email: String::from("[email protected]"),
    sign_in_count: 1,
    active: true,
};

列挙型 (Enums)

列挙型は、可能ないくつかのデータ型のうちの1つを定義することを可能にします。

例

enum IpAddrKind {
    V4,
    V6,
}

let four = IpAddrKind::V4;
let six = IpAddrKind::V6;

パターンマッチ (match)

match は Rust における強力な制御フローツールであり、switch 文に似ています。

例

enum Coin {
    Penny,
    Nickel,
    Dime,
    Quarter,
}

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

エラーハンドリング

Rust には主に2つのエラーハンドリング方法があります:Result<T, E>とOption<T>。

Result:

例

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

fn divide(a: i32, b: i32) -> Result<i32, String> {
    if b == 0 {
        Err(String::from("Division by zero"))
    } else {
        Ok(a / b)
    }
}

Option:

例

fn get_element(index: usize, vec: &Vec<i32>) -> Option<i32> {
    if index < vec.len() {
        Some(vec[index])
    } else {
        None
    }
}

所有権と借用のライフタイム

Rust はライフタイムを使用して参照の有効性を保証します。ライフタイムの注釈は'aなどで表されますが、一般的な場合にはコンパイラが自動的に推論します。

例

fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}

シャドーイング(Shadowing)

シャドーイングの概念は、他のオブジェクト指向言語の「オーバーライド」(Override)や「オーバーロード」(Overload)とは異なります。シャドーイングとは、先ほど説明した「再バインド」のことです。引用符を付けたのは、この概念を紹介する前に代わりに説明するためです。

シャドーイングとは、変数名を再利用できる仕組みのことです:

例

fn main() {
    let x = 5;
    let x = x + 1;
    let x = x * 2;
    println!("The value of x is: {}", x);
}

このプログラムの実行結果:

The value of x is: 12

シャドーイングは可変変数への代入とは概念が異なります。シャドーイングとは、同じ名前で別の変数エンティティを再表現することで、その型、可変性、値を変更できます。しかし、可変変数への代入は値の変更のみが可能です。

let mut s = "123";
s = s.len();

このプログラムはエラーになります:文字列変数に整数型の値を代入できないからです。

その他の拡張