Rust ノート: Rust 言語の構造とオブジェクト指向プログラミング

錆びたメモ
Rustの構造体

著者: Li Juncai (jcLee95) : https://blog.csdn.net/qq_28550263?spm=1001.2101.3001.5343
電子メール : [email protected]
この記事のアドレス: https://blog.csdn.net/qq_28550263/article /詳細/130876493


【介绍】:本文先介绍 Rust 语言中的 结构体 的基本用法,然后重点介绍了在 Rust 语言中通过结构体实现面向对象编程的思想、方法并给出了相当多的代码示范。

前のセクション: Rust の所有権 | 次のセクション: Rust の列挙型


1. 構造を始める

1.1 構造体とは

構造体は、さまざまなタイプのデータを組み合わせて新しいタイプを形成できるようにするカスタム データ タイプです。

1.2 構造体の定義と使用法

Rustでは、structキーワードを使用して構造体を定義し、構造体内にそのフィールドを定義できます。

struct Person {
    
    
    name: String,
    age: u32,
}

上記のコードは、Person という名前の構造体を定義します。この構造体には、name と age という 2 つのフィールドがあり、それぞれ文字列型と符号なし整数型に対応します。

構造体のインスタンスを作成するには、次を使用できます。

let person1 = Person {
    
    
    name: String::from("Alice"),
    age: 25,
};

上記の例では、person1 という名前の Person 構造体のインスタンスを作成し、そのフィールドの値を初期化しました。

1.3 構造体を使用する理由

構造を使用して、関連するデータを組織化されたデータ構造にグループ化します。構造体を使用すると、コードの構成と読みやすさが向上し、メソッドを定義して構造体に特定の動作を実装することもできます。

1.4 開始例: 単純な構造の定義

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

上記のコードは、Point という名前の構造体を定義します。この構造体には、x と y の 2 つのフィールドがあり、型は f32 (単精度浮動小数点数) です。

この構造体のインスタンスを作成し、そのフィールドにアクセスできます。

let origin = Point {
    
     x: 0.0, y: 0.0 };
println!("Origin: ({}, {})", origin.x, origin.y);

上の例では、origin という名前の Point 構造体のインスタンスを作成し、. 演算子を介してそのフィールドの値にアクセスしました。

この構造により、複数の関連フィールドを簡単に組み合わせてカスタム データ型を形成し、これらの型のインスタンスを操作とアクセスに使用できます。これにより、コードの可読性と保守性が向上すると同時に、データと動作がより適切に整理されます。

2. 構造体のメンバー

2.1 メンバー変数

構造体のメンバー変数は、整数、浮動小数点数、文字列などのさまざまなタイプのデータを格納するために使用されます。メンバー変数は、構造体インスタンスのフィールド名を通じてアクセスおよび操作できます。

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

上記のコードは、Rectangle という名前の構造体を定義します。この構造体には、幅と高さの 2 つのメンバー変数があり、型は符号なし 32 ビット整数である u32 です。

let rect = Rectangle {
    
    
    width: 10,
    height: 20,
};

上記の例では、rect という Rectangle 構造体のインスタンスを作成し、その幅と高さのメンバー変数の値を初期化しました。

println!("Width: {}", rect.width);
println!("Height: {}", rect.height);

.演算子を介して、構造体インスタンスのメンバー変数にアクセスし、その値を取得できます。

2.2 メンバーメソッド

構造体では、関連関数とも呼ばれるメンバー メソッドを定義できます。メンバー メソッドは、構造体に対して特定の操作を実行するために使用され、メンバー変数や構造体の他のメソッドにアクセスできます。

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

上記のコードでは、impl ブロッ​​クを通じて Rectangle 構造体の area と呼ばれるメソッドを実装しました。このメソッドは、長方形の面積を計算するために使用され、&self パラメーター (つまり、構造体インスタンスの借用) を取得し、u32 型の値を返します。

let rect = Rectangle {
    
    
    width: 10,
    height: 20,
};

println!("Area: {}", rect.area());

上の例では、rect という Rectangle 構造体のインスタンスを作成し、area メソッドを呼び出してその面積を計算しました。. 演算子を使用すると、構造体インスタンスのメソッドを呼び出し、戻り値を取得できます。

2.3 高度な例: 変数とメソッドを使用した構造体の定義

struct Circle {
    
    
    radius: f32,
}

impl Circle {
    
    
    fn new(radius: f32) -> Circle {
    
    
        Circle {
    
     radius }
    }

    fn area(&self) -> f32 {
    
    
        3.14 * self.radius * self.radius
    }
}
fn main() {
    
    
    let circle = Circle::new(5.0);
    println!("Area: {}", circle.area());
}

上の例では、半径を表すメンバー変数 radius を持つ Circle という名前の構造体を定義しました。また、Circle 構造体にインスタンスを作成するための関連関数を新たに実装し、円の面積を計算するためのメンバー メソッド area を定義しました。

main 関数では、Circle::new 関連関数を使用して Circle 構造体のインスタンスを作成し、area メソッドを呼び出してその面積を計算します。

構造体は、メンバー変数とメンバー メソッドを通じて、データを保存および操作するための便利な方法を提供します。構造体のメンバー変数にはさまざまなタイプのデータを格納でき、メンバー メソッドは構造体に対して特定の操作を実行できるため、コードがより明確でモジュール化されます。

3. 構造体のインスタンス化

3.1 構造体インスタンスの作成

構造体のインスタンスを作成するには、構造体名を使用し、メンバー変数を値で初期化します。

struct Car {
    
    
    make: String,
    model: String,
    year: u32,
}

上記のコードは、Car という名前の構造体を定義します。この構造体には、文字列型と符号なし 32 ビット整数型にそれぞれ対応する、make、model、year という 3 つのメンバー変数があります。

let car = Car {
    
    
    make: String::from("Toyota"),
    model: String::from("Camry"),
    year: 2021,
};

上記の例では、car という名前の Car 構造体のインスタンスを作成し、そのメンバー変数の値を初期化しました。メンバー変数名と対応する値を中括弧で囲んで指定することで、構造体を初期化できます。

3.2 構造体インスタンスの初期化

構造体のメンバー変数の一部のみを初期化することを選択できます。初期化時に、指定されていないメンバー変数はデフォルト値を使用します。

let car = Car {
    
    
    make: String::from("Toyota"),
    model: String::from("Camry"),
    ..Default::default()
};

上記の例では、Default トレイトのデフォルト メソッドを使用して、残りのメンバー変数をデフォルト値に初期化しました。

3.3 使用例: 構造体のインスタンス化と初期化

struct Person {
    
    
    name: String,
    age: u32,
    city: String,
}

impl Person {
    
    
    fn new(name: String, age: u32, city: String) -> Person {
    
    
        Person {
    
    
            name,
            age,
            city,
        }
    }
}
fn main() {
    
    
    let person1 = Person::new(String::from("Alice"), 25, String::from("New York"));
    let person2 = Person {
    
    
        name: String::from("Bob"),
        ..person1
    };

    println!("Person 1: {}, {}, {}", person1.name, person1.age, person1.city);
    println!("Person 2: {}, {}, {}", person2.name, person2.age, person2.city);
}

上の例では、name、age、city の 3 つのメンバー変数を持つ person という名前の構造体を定義しました。インスタンスの作成とそのメンバー変数の初期化に使用される、Person 構造体用の新しい関連関数を実装します。

main 関数では、person::new 関連関数を使用して、person1 という名前の Person 構造体のインスタンスを作成します。次に、構造体初期化構文を使用して person2 を person1 の値で初期化します。ここでは、name フィールドのみが指定され、他のフィールドは person1 の対応する値を使用します。

構造体のインスタンス化と初期化により、必要に応じてさまざまなプロパティを持つ構造体インスタンスを作成し、そのメンバー変数を柔軟に操作してアクセスすることができます。これにより、より多くの制御機能とカスタマイズ機能が提供されます。

4. 構造の実現

4.1 構造体のメソッドの実装

Rust では、構造体インスタンスが特定の動作を実行できるようにする構造体用のメソッドを実装できます。

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

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

    fn is_square(&self) -> bool {
    
    
        self.width == self.height
    }
}

上記のコードは Rectangle という構造体を定義し、その構造体に対して area と is_square という 2 つのメソッドを実装します。area メソッドは長方形の面積を計算し、is_square メソッドは長方形が正方形かどうかを判断します。

let rect = Rectangle {
    
    
    width: 10,
    height: 20,
};

println!("Area: {}", rect.area());
println!("Is Square: {}", rect.is_square());

上記の例では、rectという名前のRectangle構造体のインスタンスを作成し、areaメソッドとis_squareメソッドを呼び出すことで長方形の面積を取得し、正方形であるかどうかを判定しています。

構造体のメソッドを実装することで、特定の動作を構造体に関連付けることができるため、構造体のインスタンスはメソッドを直接呼び出して、対応する操作を実行できます。

4.2 構造体のトレイトの実装

構造体にカスタム メソッドを実装することに加えて、構造体に特定の特性を実装することもできるため、構造体にさらに多くの機能と動作を与えることができます。

struct Circle {
    
    
    radius: f64,
}

trait Shape {
    
    
    fn area(&self) -> f64;
}

impl Shape for Circle {
    
    
    fn area(&self) -> f64 {
    
    
        3.14 * self.radius * self.radius
    }
}

上記のコードは、Circle という構造を定義し、それに Shape 特性を実装します。Shape トレイトには、形状の面積を計算する area メソッドが含まれています。

let circle = Circle {
    
     radius: 5.0 };

println!("Area: {}", circle.area());

上記の例では、circle という Circle 構造体のインスタンスを作成し、area メソッドを呼び出して円の面積を取得しました。

構造体のトレイトを実装することで、特定の動作と機能を抽象化し、それをさまざまな構造体に適用できます。これにより、コードの再利用とモジュール性の向上が可能になります。

4.3 例: 構造体のメソッドとトレイトの実装

struct Square {
    
    
    side_length: u32,
}

trait Shape {
    
    
    fn area(&self) -> u32;
    fn perimeter(&self) -> u32;
}

impl Shape for Square {
    
    
    fn area(&self) -> u32 {
    
    
        self.side_length * self.side_length
    }

    fn perimeter(&self) -> u32 {
    
    
        4 * self.side_length
    }
}

impl Square {
    
    
    fn is_square(&self) -> bool {
    
    
        self.side_length > 0 && self.side_length == self.perimeter() / 4
    }
}
fn main() {
    
    
    let square = Square {
    
     side_length: 5 };

    println!("Area: {}", square.area());
    println!("Perimeter: {}", square.perimeter());
    println!("Is Square: {}", square.is_square());
}

上の例では、Square という構造体を定義し、それに Shape 特性と is_square メソッドを実装しました。Shape 特性には、面積と周長を計算するメソッドが含まれています。

main 関数では、square という名前の Square 構造体インスタンスを作成し、area、perimeter、および is_square メソッドを呼び出して面積、周囲を取得し、それが正方形であるかどうかを判断します。

構造体のメソッドと特性を実装することで、構造体の機能を拡張してより多くの動作や機能を持たせることができ、コードの可読性と保守性も向上させることができます。

5. 構造体とオブジェクト指向

5.1 オブジェクト指向の機能: 抽象化、カプセル化、継承、ポリモーフィズム

オブジェクト指向プログラミング (OOP) は、次の特徴を持つプログラミング パラダイムです。

  • 抽象化: 現実世界のオブジェクトをクラスとオブジェクト概念に抽象化することで、複雑な問題を理解しやすく扱いやすいモジュールに単純化できます。
  • カプセル化: データと操作をオブジェクトにカプセル化します。これにより、内部実装の詳細が外部から隠蔽され、外部と対話するための限られたインターフェイスのみが提供されます。
  • 継承: 親クラスとサブクラスの関係を定義することで、サブクラスは親クラスのプロパティやメソッドを継承することができ、コードの再利用や拡張を実現します。
  • ポリモーフィズム: 同じ操作で異なるオブジェクト タイプに応じて異なる動作を実行できるため、コードの柔軟性とスケーラビリティが向上します。

5.2 構造の継承

Rust には構造体継承のための直接的なメカニズムはありませんが、同様の機能を他の方法で実現できます。

5.2.1 タプル構造

タプル構造は、関連する値のセットを表すために使用できる名前付きフィールドのない構造のセットと考えることができます。

struct Person(String, u32);

fn main() {
    
    
    let person = Person(String::from("Alice"), 25);

    println!("Name: {}", person.0);
    println!("Age: {}", person.1);
}

上の例では、文字列型と符号なし 32 ビット整数型を含む person という名前のタプル構造を定義しました。タプルの要素にインデックスでアクセスすることで、対応する値を取得できます。

5.2.2 構造体のネスト

構造体を互いに入れ子にして階層関係を形成することで、継承と同様の効果を得ることができます。

struct Person {
    
    
    name: String,
    age: u32,
}

struct Employee {
    
    
    person: Person,
    employee_id: u32,
}

fn main() {
    
    
    let person = Person {
    
    
        name: String::from("Alice"),
        age: 25,
    };

    let employee = Employee {
    
    
        person,
        employee_id: 12345,
    };

    println!("Name: {}", employee.person.name);
    println!("Age: {}", employee.person.age);
    println!("Employee ID: {}", employee.employee_id);
}

上の例では、Person という名前の構造体と Employee という名前の構造体を定義しました。Employee 構造体には、Person 型のメンバー変数 person と、employee_id メンバー変数が含まれています。

構造をネストすることで、1 つの構造に別の構造を含めることができ、継承に似た関係を実現し、コードをより構造化してモジュール化することができます。

5.2.3 実践例: 構造の継承とネスト

struct Shape {
    
    
    color: String,
}

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

struct Circle {
    
    
    shape: Shape,
    radius: f64,
}

fn main() {
    
    
    let red_rectangle = Rectangle {
    
    
        shape: Shape {
    
    
            color: String::from("red"),
        },
        width: 10,
        height: 20,
    };

    let blue_circle = Circle {
    
    
        shape: Shape {
    
    
            color: String::from("blue"),
        },
        radius: 5.0,
    };

    println!("Rectangle: {} x {}", red_rectangle.width, red_rectangle.height);
    println!("Circle: radius {}", blue_circle.radius);
    println!("Color: Rectangle - {}, Circle - {}", red_rectangle.shape.color, blue_circle.shape.color);
}

上の例では、Shape という名前の構造と、2 つの派生構造 Rectangle と Circle を定義しました。Rectangle 構造と Circle 構造はそれぞれ Shape 構造をネストすることで、継承とコードの再利用の効果を実現します。

構造の継承とネストを使用することで、Rust でオブジェクト指向プログラミングの特性をシミュレートし、コードをより柔軟で拡張可能にすることができます。

5.3 構造を使用してオブジェクトを記述する

構造体を使用すると、属性 (フィールド) と動作 (メソッド) を持つことができる現実世界のオブジェクトを記述および表現できます。

struct Person {
    
    
    name: String,
    age: u32,
}

impl Person {
    
    
    fn introduce(&self) {
    
    
        println!("My name is {} and I am {} years old.", self.name, self.age);
    }
}
fn main() {
    
    
    let person = Person {
    
    
        name: String::from("Alice"),
        age: 25,
    };

    person.introduce();
}

上の例では、name と age の 2 つのフィールドを持つ person という名前の構造体を定義しました。Person 構造体の導入メソッドを実装することにより、オブジェクト上でこのメソッドを呼び出して、オブジェクト自体を紹介することができます。

main 関数では、person という名前の Person 構造体のインスタンスを作成し、introduction メソッドを呼び出して自己紹介を出力します。

構造を使用してオブジェクトを記述することにより、オブジェクトのプロパティと動作を一緒にカプセル化して、データと操作の高い一貫性を実現し、コードをより明確で保守しやすくすることができます。

5.4 クラスベースの記述オブジェクトとの比較

Rust の構造体はオブジェクト指向プログラミングのクラスに似ていますが、いくつかの違いもあります。次に本章では、 Javaに代表される代表的なクラスベースのオブジェクト指向プログラミング言語と以下の観点から比較していきます。

  • カプセル化
  • 継承
  • 多態性
  • 所有権システム

5.4.1 カプセル化

Rust の構造体は、 pub キーワードを使用してフィールドとメソッドの可視性を制御し、カプセル化を実現できます。一方、オブジェクト指向クラスにはデフォルトでパブリック インターフェイスがあり、外部からアクセスできます。

Rust言語 Javaのようなオブジェクト指向言語
Rust の構造体は、 pub キーワードを使用してフィールドとメソッドの可視性を制御し、カプセル化を実現できます。デフォルトでは、構造体のフィールドとメソッドはプライベートであり、同じモジュール内でのみアクセスできます。 Java などのオブジェクト指向言語: オブジェクト指向言語は通常、アクセス修飾子 (public、private など) を使用して、クラスのメンバーの可視性を制御します。クラスのメンバーには、他のクラスまたはオブジェクトからアクセスできます。

5.4.2 継承

Rust の構造体には直接継承メカニズムがありませんが、構造体のネストとトレイトの実装によって同様の効果を実現できます。

Rust言語 Javaのようなオブジェクト指向言語
Rust の構造体には直接継承メカニズムがありません。ただし、構造体をネストしてトレイトを実装することで、同様の機能を実現できます。構造体をネストすることにより、他の構造体をフィールドとして含む構造体を作成できます。 オブジェクト指向言語の継承により、あるクラスが別のクラスを派生できるため、コードの再利用が可能になります。サブクラスは親クラスのプロパティとメソッドを継承し、これらのメンバーを追加またはオーバーライドできます。

5.4.3 ポリモーフィズム

Rust はトレイトとジェネリクスを通じてポリモーフィズムを実装しており、異なる型の構造体でも同じトレイトを実装し、同じ方法で処理できます。

Rust言語 Javaのようなオブジェクト指向言語
Rust は、トレイトとジェネリックを通じてポリモーフィズムを実装します。異なるタイプの構造体が同じ特性を実装し、同じ方法で処理できます。これにより、コードがより柔軟になり、さまざまな種類のオブジェクトを処理できるようになります。 オブジェクト指向言語は、継承とインターフェイスを使用してポリモーフィズムを実現します。サブクラスは親クラスの代わりに使用でき、多態的に使用できます。

5.4.4 所有権システム

Rust の所有権システムにより、オブジェクトの操作がより安全かつ効率的になり、メモリ管理の問題が回避されます。

Rust言語 Javaのようなオブジェクト指向言語
Rust の所有権システムにより、メモリの安全性とリソース管理が保証されます。構造体は Rust で所有されており、構造体が破棄されると、そのリソースが解放されます。この所有権システムにより、オブジェクトの操作がより安全かつ効率的になります。 オブジェクト指向言語は通常、ガベージ コレクションまたは手動メモリ管理を使用して、オブジェクトの有効期間とメモリ使用量を管理します。

6. まとめ

この記事では、Rust における構造体の概念、使用法、実際の応用について詳しく説明しました。最初に、構造体の宣言方法、フィールドとメソッドの定義方法など、構造体の基本的な定義と使用方法を紹介し、サンプル コードを使用して構造体の基本操作を示しました。次に、メンバー変数やメンバー メソッドなど、構造体のメンバーについて詳しく説明しました。構造体内でフィールドとメソッドを定義する方法を紹介し、構造体のメンバーを使用して操作およびアクセスする方法を示しました。次に、構造体インスタンスの作成や構造体インスタンスの初期化など、構造体のインスタンス化について説明しました。新しい関数と簡素化された初期化構文による構造体インスタンスの作成を紹介し、サンプル コードを通じてさまざまなインスタンス化方法を示しました。

次に、構造体のメソッドと特性の実装を含め、構造体の実装について検討しました。構造体のメソッドを定義する方法を詳しく説明し、サンプル コードを使用してメソッドの使用法を示します。同時に、構造体の機能と動作を拡張するために構造体のトレイトを実装する方法についても議論しました。最後に、オブジェクト指向プログラミングを比較し、構造とオブジェクト指向プログラミングの特徴と違いについて説明しました。構造の継承とネストの実装を紹介し、サンプル コードを通じて Rust でオブジェクト指向機能をシミュレートする方法を示しました。

おすすめ

転載: blog.csdn.net/qq_28550263/article/details/130876493