English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Rust のエnumクラス

列挙型はRustでは他のプログラミング言語の概念と比べて単純ではありませんが、非常に簡単に使用できます:

#[derive(Debug)]

enum Book {
    Papery, Electronic
}

fn main() {
    let book = Book::Papery;
    println!("{:?}", book);
}

実行結果:

Papery

書は、紙質書(Papery book)と電子書(Electronic book)の二種類に分類されます。

如果你现在正在开发一个图书管理系统,你需要描述两种书的不同属性(纸质书有索书号,电子书只有 URL),你可以为枚举类成员添加元组属性描述:

enum Book {
    Papery(u32),
    Electronic(String),
}
let book = Book::Papery(1001);
let ebook = Book::Electronic(String::from("url://..."));

如果你想为属性命名,可以用结构体语法:

enum Book {
    Papery { index: u32 },
    Electronic { url: String },
}
let book = Book::Papery{index: 1001};

このように名前を設定することはできますが、構造体のフィールドのように列挙クラスにバインドされた属性にアクセスすることはできません。アクセス方法はmatch文法内にあります。

match文法

列挙の目的は、ある種の事象の分類であり、分類の目的は、異なる状況を説明することです。この原理に基づいて、列挙クラスは最終的には分岐構造で処理されます(多くの言語でのswitch)。switch文法は非常に伝統的ですが、Rustではサポートされていません。多くの言語がswitchを放棄する理由は、switchがbreakを忘れることで連続して実行される問題が発生するためです。JavaやC#などの言語では、安全なチェックを通じてこのような問題を防ぎます。

Rustはmatch文を使って分岐構造を実現します。まず、matchで列挙クラスを処理する方法を学びましょう:

fn main() {
    enum Book {
        Papery {index: u32},
        Electronic {url: String},
    }
   
    let book = Book::Papery{index: 1001};
    let ebook = Book::Electronic{url: String::from("url...")};
   
    match book {
        Book::Papery { index } => {
            println!("Papery book {}", index);
        },
        Book::Electronic { url } => {
            println!("E-book {}", url);
        }
    }
}

実行結果:

Papery book 1001

matchブロックは、機能表現としても扱うことができます。また、返値を持つこともできます:

match列挙クラスの例 {
    分類1 => 返値表現 ,
    分類2 => 返値表現 ,
    ...
}

ただし、すべての返値表現のデータ型は同じでなければなりません!

もし列挙クラスの付属属性をタプルとして定義すると、matchブロック内で一時的に名前を指定する必要があります:

enum Book {
    Papery(u32),
    Electronic {url: String},
}
let book = Book::Papery(1001);

match book {
    Book::Papery(i) => {
        println!("{}", i);
    },
    Book::Electronic { url } => {
        println!("{}", url);
    }
}

matchは、列挙クラスに対する分岐選択に加えて、整数、浮点数、文字、文字列スライス引用(&str)のデータ型に対しても分岐選択ができます。浮点数のデータ型に対する分岐選択は合法ですが、精度問題が原因で分岐エラーが発生する可能性があるため、推奨しません。

非列挙クラスに対する分岐選択を行う際には、例外処理に注意を払う必要があります。例外処理はアンダースコア _ で表されます:

fn main() {
    let t = "abc";
    match t {
        "abc" => println!("Yes"),
        _ => {},
    }
}

Option エンümクラス

Option は Rust 标準ライブラリのエンümクラスであり、Rust が null リファレンスをサポートしていない空白を埋めるために使用されます。

多くの言語が null の存在をサポートしています(C/C++(Java)、非常に便利ですが、非常に大きな問題も引き起こします。null の発明者もこの点を認めています、「便利なアイデアが累積的な問題を引き起こす」と述べています。 10 数十億ドルの損失"。

null は開発者がすべてを null でないと考えているときにプログラムに致命的なダメージを与えます:このようなエラーが発生すると、プログラムの実行は完全に停止します。

この問題を解決するために、多くの言語はデフォルトで null を認めず、言語レベルで null の出現をサポートしています(通常、型の前に ? 符号を使用)。

Java はデフォルトで null をサポートしていますが、@NotNull アノテーションを通じて null の出現を制限することができます。これは一種の応急処置です。

Rust は言語レベルで null の存在を完全に禁止していますが、null は少ない問題を効率的に解決できるため、Rust は Option エンümクラスを導入しています:

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

空値を持つことができるクラスを定義する場合は、以下のようにします:

let opt = Option::Some("Hello");

opt に対して特定の操作を実行する場合、まずそれが何かを判断する必要があります: Option::None

fn main() {
    let opt = Option::Some("Hello");
    match opt {
        Option::Some(something) => {
            println!("{}", something);
        },
        Option::None => {
            println!("opt は何もありません");
        }
    }
}

実行結果:

Hello

もし変数が最初に空値で始まるなら、コンパイラに思いやりを持ってください。コンパイラは値が空でない場合の変数の型をどうやって知ることができますか?

したがって、初期値が空の Option は明確な型を持ちます:

fn main() {
    let opt: Option<&str> = Option::None;
    match opt {
        Option::Some(something) => {
            println!("{}", something);
        },
        Option::None => {
            println!("opt は何もありません");
        }
    }
}

実行結果:

opt は何もありません

この設計は空値のプログラミングを難しくしますが、これは安定した効率的なシステムを構築するために必要です。Option は Rust コンパイラがデフォルトで導入するため、使用時に Option:: を省略して None または Some() で直接書くことができます。

Option は特別なエンümクラスであり、値の分岐選択を含むことができます:

fn main() {
        let t = Some(64);
        match t {
                Some(64) => println!("Yes"),
                _ => println!("No"),
        }
}

if let 文法

let i = 0;
match i {
    0 => println!("zero"),
    _ => {},
}

メイン関数内に実行結果を入れる:

zero

このプログラムの目的は、i が数字 0 かどうかを判断し、そうであれば「zero」を印刷することです。

このコードを if let 文法で短くします:

let i = 0;
if let 0 = i {
    println!("zero");
}

if let のフォーマットは以下の通りです:

if let 匹配値 = 源変数 {
    文
}

例外を処理するために、else ブロックを追加できます。

if let は、2つの条件のみを区別する match 文の「語法糖」です(語法糖は、同じ原理を持つ語法の便利な代替品です)。

エnumクラスに対しても適用できます:

fn main() {
    enum Book {
        Papery(u32),
        Electronic(String)
    }
    let book = Book::Electronic(String::from("url"));
    if let Book::Papery(index) = book {
        println!("Papery {}", index);
    } else {
        println!("Not papery book");
    }
}