さび-クイックガイド
Rustは、GraydonHoareによって開発されたシステムレベルのプログラミング言語です。その後、MozillaLabsがプログラムを買収しました。
アプリケーションv / sシステムプログラミング言語
Java / C#などのアプリケーションプログラミング言語は、ユーザーに直接サービスを提供するソフトウェアの構築に使用されます。これらは、スプレッドシート、ワードプロセッサ、Webアプリケーション、モバイルアプリケーションなどのビジネスアプリケーションの構築に役立ちます。
C / C ++などのシステムプログラミング言語は、ソフトウェアおよびソフトウェアプラットフォームの構築に使用されます。これらは、オペレーティングシステム、ゲームエンジン、コンパイラなどの構築に使用できます。これらのプログラミング言語には、高度なハードウェアの相互作用が必要です。
システムとアプリケーションプログラミング言語は2つの大きな問題に直面しています-
- 安全なコードを書くのは難しいです。
- マルチスレッドコードを書くのは難しいです。
なぜ錆びるの?
Rustは3つの目標に焦点を当てています-
- Safety
- Speed
- Concurrency
この言語は、信頼性が高く高速なソフトウェアを簡単な方法で開発するために設計されました。Rustを使用して、高レベルのプログラムをハードウェア固有のプログラムまで作成できます。
パフォーマンス
Rustプログラミング言語には、設計上ガベージコレクター(GC)がありません。これにより、実行時のパフォーマンスが向上します。
コンパイル時のメモリの安全性
Rustを使用して構築されたソフトウェアは、ダングリングポインター、バッファーオーバーラン、メモリリークなどのメモリの問題から安全です。
マルチスレッドアプリケーション
Rustの所有権とメモリ安全性のルールは、データの競合なしに並行性を提供します。
Web Assembly(WASM)のサポート
Web Assemblyは、ブラウザ、組み込みデバイス、またはその他の場所で、計算量の多いアルゴリズムを実行するのに役立ちます。ネイティブコードの速度で実行されます。RustをWebAssemblyにコンパイルして、高速で信頼性の高い実行を実現できます。
Rustのインストールは簡単になります rustup、Rustバージョンおよび関連ツールを管理するためのコンソールベースのツール。
Windowsへのインストール
WindowsにRUSTをインストールする方法を学びましょう。
WindowsでRustプログラムを実行するには、C ++ツールを使用したVisualStudio2013以降のインストールが必須です。まず、ここからVisualStudioをダウンロードしますVS2013 Express
ダウンロードとインストール rustup Windows用のツール。 rustup-init.exeこちらからダウンロードできます-RustLang
ダブルクリック rustup-init.exeファイル。クリックすると以下の画面が表示されます。
デフォルトのインストールでは、Enterキーを押します。インストールが完了すると、次の画面が表示されます。
インストール画面から、Rust関連のファイルがフォルダに保存されていることがわかります-
C:\ Users \ {PC} \。cargo \ bin
フォルダの内容は-
cargo-fmt.exe
cargo.exe
rls.exe
rust-gdb.exe
rust-lldb.exe
rustc.exe // this is the compiler for rust
rustdoc.exe
rustfmt.exe
rustup.exe
CargoRustのパッケージマネージャーです。次のことを確認するにはcargo がインストールされている場合は、次のコマンドを実行します-
C:\Users\Admin>cargo -V
cargo 1.29.0 (524a578d7 2018-08-05)
Rustのコンパイラは rustc。コンパイラのバージョンを確認するには、次のコマンドを実行します-
C:\Users\Admin>cargo -V
cargo 1.29.0 (524a578d7 2018-08-05)
Linux / Macへのインストール
インストールするには rustup LinuxまたはmacOSでは、ターミナルを開いて次のコマンドを入力します。
$ curl https://sh.rustup.rs -sSf | sh
コマンドはスクリプトをダウンロードし、インストールを開始します rustupRustの最新の安定バージョンをインストールするツール。パスワードの入力を求められる場合があります。インストールが成功すると、次の行が表示されます-
Rust is installed now. Great!
インストールスクリプトは、次回のログイン後にRustをシステムPATHに自動的に追加します。ターミナルを再起動する代わりにRustの使用をすぐに開始するには、シェルで次のコマンドを実行して、RustをシステムのPATHに手動で追加します-
$ source $HOME/.cargo/env
または、次の行を〜/ .bash_profile −に追加することもできます。
$ export PATH="$HOME/.cargo/bin:$PATH"
NOTE − Rustプログラムをコンパイルしようとして、リンカーを実行できなかったことを示すエラーが発生した場合、それはリンカーがシステムにインストールされていないことを意味し、手動でインストールする必要があります。
チュートリアルの使用RUSTのポイントコーディンググラウンド
Read-Evaluate-Print Loop(REPL)は、コンピュータープログラムをコンパイルおよび実行するための使いやすいインタラクティブシェルです。ブラウザ内でRustプログラムをオンラインでコンパイルして実行する場合は、Tutorialspoint CodingGroundを使用してください。
この章では、Rust言語の基本的な構文について説明します。 HelloWorld 例。
作成する HelloWorld-App フォルダを開き、ターミナルでそのフォルダに移動します
C:\Users\Admin>mkdir HelloWorld-App
C:\Users\Admin>cd HelloWorld-App
C:\Users\Admin\HelloWorld-App>
Rustファイルを作成するには、次のコマンドを実行します-
C:\Users\Admin\HelloWorld-App>notepad Hello.rs
Rustプログラムファイルの拡張子は.rsです。上記のコマンドは空のファイルを作成しますHello.rsそしてそれをNOTEpadで開きます。このファイルに以下のコードを追加します-
fn
main(){
println!("Rust says Hello to TutorialsPoint !!");
}
上記のプログラムは、関数main fn main()を定義しています。FNキーワードは、関数を定義するために使用されます。)(主は、プログラムへのエントリポイントとして動作する予め定義された関数です。println!Rustで事前定義されたマクロです。文字列(ここではHello)をコンソールに出力するために使用されます。マクロ呼び出しには常に感嘆符が付いています– !。
コンパイルする Hello.rs を使用してファイル rustc。
C:\Users\Admin\HelloWorld-App>rustc Hello.rs
プログラムが正常にコンパイルされると、実行可能ファイル(file_name.exe)が生成されます。.exeファイルが生成されているかどうかを確認するには、次のコマンドを実行します。
C:\Users\Admin\HelloWorld-App>dir
//lists the files in folder
Hello.exe
Hello.pdb
Hello.rs
- Hello.exeファイルを実行し、出力を確認します。
マクロとは何ですか?
Rustは、メタプログラミングを可能にする強力なマクロシステムを提供します。前の例で見たように、マクロは名前がbang(!)で終わることを除いて関数のように見えますが、関数呼び出しを生成する代わりに、マクロはプログラムの残りの部分でコンパイルされるソースコードに展開されます。したがって、関数とは異なり、プログラムにより多くのランタイム機能を提供します。マクロは関数の拡張バージョンです。
printlnを使用してください!マクロ-構文
println!(); // prints just a newline
println!("hello ");//prints hello
println!("format {} arguments", "some"); //prints format some arguments
Rustのコメント
コメントは、プログラムの読みやすさを向上させる方法です。コメントを使用して、コードの作成者、関数/構成に関するヒントなど、プログラムに関する追加情報を含めることができます。コンパイラーはコメントを無視します。
Rustは次のタイプのコメントをサポートします-
単一行コメント(//)-//と行末の間のテキストはコメントとして扱われます
複数行コメント(/ * * /)-これらのコメントは複数行にまたがることがあります。
例
//this is single line comment
/* This is a
Multi-line comment
*/
オンラインで実行
Rustプログラムは、Tutorialspoint CodingGroundを介してオンラインで実行できます。[エディター]タブでHelloWorldプログラムを作成し、[実行]をクリックして結果を表示します。
型システムは、言語でサポートされているさまざまなタイプの値を表します。型システムは、提供された値がプログラムによって格納または操作される前に、それらの値の有効性をチェックします。これにより、コードが期待どおりに動作することが保証されます。型システムはさらに、より豊富なコードヒントと自動化されたドキュメントを可能にします。
Rustは静的に型付けされた言語です。Rustのすべての値は、特定のデータ型です。コンパイラーは、割り当てられた値に基づいて変数のデータ型を自動的に推測できます。
変数を宣言する
使用 let 変数を宣言するキーワード。
fn main() {
let company_string = "TutorialsPoint"; // string type
let rating_float = 4.5; // float type
let is_growing_boolean = true; // boolean type
let icon_char = '♥'; //unicode character type
println!("company name is:{}",company_string);
println!("company rating on 5 is:{}",rating_float);
println!("company is growing :{}",is_growing_boolean);
println!("company icon is:{}",icon_char);
}
上記の例では、変数のデータ型は、それらに割り当てられた値から推測されます。たとえば、Rustは文字列データ型を変数company_stringに割り当て、floatデータ型をrating_floatに割り当てます。
println!マクロは2つの引数を取ります-
- プレースホルダーである特別な構文{}
- 変数名または定数
プレースホルダーは変数の値に置き換えられます
上記のコードスニペットの出力は次のようになります-
company name is: TutorialsPoint
company rating on 5 is:4.5
company is growing: true
company icon is: ♥
スカラータイプ
スカラー型は単一の値を表します。たとえば、10,3.14、「c」。Rustには4つの主要なスカラータイプがあります。
- Integer
- Floating-point
- Booleans
- Characters
各タイプについては、以降のセクションで学習します。
整数
整数は、小数成分のない数値です。簡単に言うと、整数データ型は整数を表すために使用されます。
整数はさらに符号付きと符号なしに分類できます。符号付き整数は、負の値と正の値の両方を格納できます。符号なし整数は正の値のみを格納できます。整数型の場合の詳細な説明を以下に示します-
シニア番号 | サイズ | 署名済み | 署名なし |
---|---|---|---|
1 | 8ビット | i8 | u8 |
2 | 16ビット | i16 | u16 |
3 | 32ビット | i32 | u32 |
4 | 64ビット | i64 | u64 |
5 | 128ビット | i128 | u128 |
6 | アーチ | isize | 使用する |
整数のサイズはarchにすることができます。これは、データ型のサイズがマシンのアーキテクチャから派生することを意味します。サイズがarchの整数は、x86マシンでは32ビット、x64マシンでは64ビットになります。アーチ整数は、主に、ある種のコレクションにインデックスを付けるときに使用されます。
図
fn main() {
let result = 10; // i32 by default
let age:u32 = 20;
let sum:i32 = 5-15;
let mark:isize = 10;
let count:usize = 30;
println!("result value is {}",result);
println!("sum is {} and age is {}",sum,age);
println!("mark is {} and count is {}",mark,count);
}
出力は以下のようになります-
result value is 10
sum is -10 and age is 20
mark is 10 and count is 30
上記のコードは、ageの値を浮動小数点値に置き換えると、コンパイルエラーを返します。
整数範囲
署名された各バリアントは、-(2 ^(n-1)から2 ^(n-1)-1までの数値を格納できます。ここで、nはバリアントが使用するビット数です。たとえば、i8は-(2 ^)からの数値を格納できます。7)to 2 ^ 7 -1 −ここでは、nを8に置き換えました。
符号なしバリアントはそれぞれ、0から(2 ^ n)-1までの数値を格納できます。たとえば、u8は0から2 ^ 7までの数値を格納できます。これは、0から255に相当します。
整数オーバーフロー
整数オーバーフローは、整数変数に割り当てられた値がデータ型のRustで定義された範囲を超えると発生します。例を挙げてこれを理解しましょう-
fn main() {
let age:u8 = 255;
// 0 to 255 only allowed for u8
let weight:u8 = 256; //overflow value is 0
let height:u8 = 257; //overflow value is 1
let score:u8 = 258; //overflow value is 2
println!("age is {} ",age);
println!("weight is {}",weight);
println!("height is {}",height);
println!("score is {}",score);
}
符号なしu8変数の有効な範囲は0〜255です。上記の例では、変数には255(Rustの整数変数の上限)より大きい値が割り当てられています。実行時に、上記のコードは警告を返します-warning − literal out of range for u8体重、身長、スコア変数。255以降のオーバーフロー値は0、1、2などから始まります。警告なしの最終出力は次のとおりです。
age is 255
weight is 0
height is 1
score is 2
浮く
Rustのフロートデータ型は次のように分類できます f32 そして f64。f32タイプは単精度浮動小数点数で、f64は倍精度です。デフォルトのタイプはf64です。floatデータ型について詳しく理解するには、次の例を検討してください。
fn main() {
let result = 10.00; //f64 by default
let interest:f32 = 8.35;
let cost:f64 = 15000.600; //double precision
println!("result value is {}",result);
println!("interest is {}",interest);
println!("cost is {}",cost);
}
出力は以下のようになります−
interest is 8.35
cost is 15000.6
自動型キャスト
Rustでは自動型キャストは許可されていません。次のコードスニペットについて考えてみます。整数値がfloat変数に割り当てられますinterest。
fn main() {
let interest:f32 = 8; // integer assigned to float variable
println!("interest is {}",interest);
}
コンパイラは mismatched types error 以下のように。
error[E0308]: mismatched types
--> main.rs:2:22
|
2 | let interest:f32=8;
| ^ expected f32, found integral variable
|
= note: expected type `f32`
found type `{integer}`
error: aborting due to previous error(s)
番号区切り文字
大きな数字を読みやすくするために、視覚的な区切り文字_アンダースコアを使用して数字を区切ることができます。つまり、50,000は50_000と書くことができます。これを以下の例に示します。
fn main() {
let float_with_separator = 11_000.555_001;
println!("float value {}",float_with_separator);
let int_with_separator = 50_000;
println!("int value {}",int_with_separator);
}
出力は以下のとおりです。
float value 11000.555001
int value 50000
ブール値
ブール型には、trueまたはfalseの2つの可能な値があります。使用bool ブール変数を宣言するキーワード。
図
fn main() {
let isfun:bool = true;
println!("Is Rust Programming Fun ? {}",isfun);
}
上記のコードの出力は次のようになります-
Is Rust Programming Fun ? true
キャラクター
Rustの文字データ型は、数字、アルファベット、Unicode、および特殊文字をサポートしています。使用char文字データ型の変数を宣言するキーワード。Rustのchar型は、Unicode Scalar値を表します。つまり、ASCIIだけでなく多くのことを表すことができます。Unicodeスカラー値の範囲はU+0000 に U+D7FF そして U+E000 に U+10FFFF 包括的。
Characterデータ型についてさらに理解するための例を考えてみましょう。
fn main() {
let special_character = '@'; //default
let alphabet:char = 'A';
let emoji:char = '';
println!("special character is {}",special_character);
println!("alphabet is {}",alphabet);
println!("emoji is {}",emoji);
}
上記のコードの出力は次のようになります-
special character is @
alphabet is A
emoji is
変数は、プログラムが操作できる名前付きストレージです。簡単に言えば、変数はプログラムが値を格納するのに役立ちます。Rustの変数は、特定のデータ型に関連付けられています。データ型は、変数のメモリのサイズとレイアウト、そのメモリ内に格納できる値の範囲、および変数に対して実行できる一連の操作を決定します。
変数に名前を付けるための規則
このセクションでは、変数に名前を付けるためのさまざまなルールについて学習します。
変数の名前は、文字、数字、および下線文字で構成できます。
文字またはアンダースコアで始まる必要があります。
Rustでは大文字と小文字が区別されるため、大文字と小文字は区別されます。
構文
Rustで変数を宣言するとき、データ型はオプションです。データ型は、変数に割り当てられた値から推測されます。
変数を宣言するための構文を以下に示します。
let variable_name = value; // no type specified
let variable_name:dataType = value; //type specified
図
fn main() {
let fees = 25_000;
let salary:f64 = 35_000.00;
println!("fees is {} and salary is {}",fees,salary);
}
上記のコードの出力は次のようになります fees is 25000 and salary is 35000。
不変
デフォルトでは、変数は不変です-Rustでは読み取り専用です。つまり、値が変数名にバインドされると、変数の値を変更することはできません。
例を挙げてこれを理解しましょう。
fn main() {
let fees = 25_000;
println!("fees is {} ",fees);
fees = 35_000;
println!("fees changed is {}",fees);
}
出力は以下のようになります−
error[E0384]: re-assignment of immutable variable `fees`
--> main.rs:6:3
|
3 | let fees = 25_000;
| ---- first assignment to `fees`
...
6 | fees=35_000;
| ^^^^^^^^^^^ re-assignment of immutable variable
error: aborting due to previous error(s)
エラーメッセージは、エラーの原因を示しています。不変の変動料金に値を2回割り当てることはできません。これは、Rustがプログラマーにコードを記述させ、安全性と簡単な並行性を利用できるようにする多くの方法の1つです。
可変
変数はデフォルトで不変です。変数名の前にmutキーワードを変更可能にします。可変変数の値は変更できます。
可変変数を宣言するための構文は次のとおりです。
let mut variable_name = value;
let mut variable_name:dataType = value;
Let us understand this with an example
fn main() {
let mut fees:i32 = 25_000;
println!("fees is {} ",fees);
fees = 35_000;
println!("fees changed is {}",fees);
}
スニペットの出力を以下に示します-
fees is 25000
fees changed is 35000
定数は、変更できない値を表します。定数を宣言すると、その値が変わることはありません。定数を使用するためのキーワードはconst。定数は明示的に入力する必要があります。以下は、定数を宣言するための構文です。
const VARIABLE_NAME:dataType = value;
さび定数命名規則
定数の命名規則は、変数の命名規則と似ています。定数名のすべての文字は通常大文字です。変数の宣言とは異なり、let キーワードは定数の宣言には使用されません。
以下の例では、Rustで定数を使用しています。
fn main() {
const USER_LIMIT:i32 = 100; // Declare a integer constant
const PI:f32 = 3.14; //Declare a float constant
println!("user limit is {}",USER_LIMIT); //Display value of the constant
println!("pi value is {}",PI); //Display value of the constant
}
定数v / s変数
このセクションでは、定数と変数の差別化要因について学習します。
定数は、を使用して宣言されます const 変数がを使用して宣言されている間のキーワード let キーワード。
変数宣言はオプションでデータ型を持つことができますが、定数宣言はデータ型を指定する必要があります。これは、const USER_LIMIT = 100がエラーになることを意味します。
を使用して宣言された変数 letキーワードはデフォルトで不変です。ただし、を使用して変更するオプションがありますmutキーワード。定数は不変です。
定数は定数式にのみ設定でき、関数呼び出しの結果や実行時に計算されるその他の値には設定できません。
定数は、グローバルスコープを含む任意のスコープで宣言できるため、コードの多くの部分で知る必要のある値に役立ちます。
変数と定数のシャドウイング
Rustを使用すると、プログラマーは同じ名前の変数を宣言できます。このような場合、新しい変数は前の変数をオーバーライドします。
例を挙げてこれを理解しましょう。
fn main() {
let salary = 100.00;
let salary = 1.50 ;
// reads first salary
println!("The value of salary is :{}",salary);
}
上記のコードは、salaryという名前で2つの変数を宣言しています。最初の宣言には100.00が割り当てられ、2番目の宣言には値1.50が割り当てられます。2番目の変数は、出力の表示中に最初の変数をシャドウまたは非表示にします。
出力
The value of salary is :1.50
Rustは、シャドウイング中にさまざまなデータ型の変数をサポートします。
次の例を考えてみましょう。
コードは名前で2つの変数を宣言します uname。最初の宣言には文字列値が割り当てられ、2番目の宣言には整数が割り当てられます。len関数は、文字列値の合計文字数を返します。
fn main() {
let uname = "Mohtashim";
let uname = uname.len();
println!("name changed to integer : {}",uname);
}
出力
name changed to integer: 9
変数とは異なり、定数をシャドウすることはできません。上記のプログラムの変数が定数に置き換えられると、コンパイラーはエラーをスローします。
fn main() {
const NAME:&str = "Mohtashim";
const NAME:usize = NAME.len();
//Error : `NAME` already defined
println!("name changed to integer : {}",NAME);
}
Rustの文字列データ型は次のように分類できます-
文字列リテラル(&str)
文字列オブジェクト(String)
文字列リテラル
文字列リテラル(&str)は、コンパイル時に文字列の値がわかっている場合に使用されます。文字列リテラルは文字のセットであり、変数にハードコードされています。たとえば、company = "TutorialsPoint"とします。文字列リテラルはモジュールstd :: strにあります。文字列リテラルは、文字列スライスとも呼ばれます。
次の例では、companyとlocationの2つの文字列リテラルを宣言しています。
fn main() {
let company:&str="TutorialsPoint";
let location:&str = "Hyderabad";
println!("company is : {} location :{}",company,location);
}
文字列リテラルはデフォルトで静的です。これは、文字列リテラルがプログラム全体の期間中有効であることが保証されていることを意味します。以下に示すように、変数を静的として明示的に指定することもできます。
fn main() {
let company:&'static str = "TutorialsPoint";
let location:&'static str = "Hyderabad";
println!("company is : {} location :{}",company,location);
}
上記のプログラムは次の出力を生成します-
company is : TutorialsPoint location :Hyderabad
文字列オブジェクト
文字列オブジェクトタイプは、標準ライブラリで提供されます。文字列リテラルとは異なり、文字列オブジェクトタイプはコア言語の一部ではありません。これは、標準ライブラリpub structStringのパブリック構造として定義されています。文字列は拡張可能なコレクションです。可変でUTF-8でエンコードされたタイプです。ザ・Stringオブジェクトタイプは、実行時に提供される文字列値を表すために使用できます。文字列オブジェクトはヒープに割り当てられます。
構文
Stringオブジェクトを作成するには、次の構文のいずれかを使用できます-
String::new()
上記の構文は空の文字列を作成します
String::from()
これにより、デフォルト値がパラメータとしてに渡される文字列が作成されます。 from() 方法。
次の例は、Stringオブジェクトの使用法を示しています。
fn main(){
let empty_string = String::new();
println!("length is {}",empty_string.len());
let content_string = String::from("TutorialsPoint");
println!("length is {}",content_string.len());
}
上記の例では、新しいメソッドを使用して空の文字列オブジェクトと、fromメソッドを使用して文字列リテラルから文字列オブジェクトの2つの文字列を作成します。
出力は以下のようになります−
length is 0
length is 14
一般的なメソッド-文字列オブジェクト
シニア番号 | 方法 | 署名 | 説明 |
---|---|---|---|
1 | 新着() | pub const fn new()→文字列 | 新しい空の文字列を作成します。 |
2 | to_string() | fn to_string(&self)→文字列 | 指定された値を文字列に変換します。 |
3 | replace() | pub fn replace <'a、P>(&' a self、from:P、to:&str)→文字列 | パターンのすべての一致を別の文字列に置き換えます。 |
4 | as_str() | pub fn as_str(&self)→&str | 文字列全体を含む文字列スライスを抽出します。 |
5 | push() | pub fn push(&mut self, ch: char) | Appends the given char to the end of this String. |
6 | push_str() | pub fn push_str(&mut self, string: &str) | Appends a given string slice onto the end of this String. |
7 | len() | pub fn len(&self) → usize | Returns the length of this String, in bytes. |
8 | trim() | pub fn trim(&self) → &str | Returns a string slice with leading and trailing whitespace removed. |
9 | split_whitespace() | pub fn split_whitespace(&self) → SplitWhitespace | Splits a string slice by whitespace and returns an iterator. |
10 | split() | pub fn split<'a, P>(&'a self, pat: P) → Split<'a, P> , where P is pattern can be &str, char, or a closure that determines the split. | Returns an iterator over substrings of this string slice, separated by characters matched by a pattern. |
11 | chars() | pub fn chars(&self) → Chars | Returns an iterator over the chars of a string slice. |
Illustration: new()
An empty string object is created using the new() method and its value is set to hello.
fn main(){
let mut z = String::new();
z.push_str("hello");
println!("{}",z);
}
Output
The above program generates the following output −
hello
Illustration: to_string()
To access all methods of String object, convert a string literal to object type using the to_string() function.
fn main(){
let name1 = "Hello TutorialsPoint ,
Hello!".to_string();
println!("{}",name1);
}
Output
The above program generates the following output −
Hello TutorialsPoint , Hello!
Illustration: replace()
The replace() function takes two parameters − the first parameter is a string pattern to search for and the second parameter is the new value to be replaced. In the above example, Hello appears two times in the name1 string.
The replace function replaces all occurrences of the string Hello with Howdy.
fn main(){
let name1 = "Hello TutorialsPoint ,
Hello!".to_string(); //String object
let name2 = name1.replace("Hello","Howdy"); //find and replace
println!("{}",name2);
}
Output
The above program generates the following output −
Howdy TutorialsPoint , Howdy!
Illustration: as_str()
The as_str() function extracts a string slice containing the entire string.
fn main() {
let example_string = String::from("example_string");
print_literal(example_string.as_str());
}
fn print_literal(data:&str ){
println!("displaying string literal {}",data);
}
Output
The above program generates the following output −
displaying string literal example_string
Illustration: push()
The push() function appends the given char to the end of this String.
fn main(){
let mut company = "Tutorial".to_string();
company.push('s');
println!("{}",company);
}
Output
The above program generates the following output −
Tutorials
Illustration: push_str()
The push_str() function appends a given string slice onto the end of a String.
fn main(){
let mut company = "Tutorials".to_string();
company.push_str(" Point");
println!("{}",company);
}
Output
The above program generates the following output −
Tutorials Point
Illustration: len()
The len() function returns the total number of characters in a string (including spaces).
fn main() {
let fullname = " Tutorials Point";
println!("length is {}",fullname.len());
}
Output
The above program generates the following output −
length is 20
Illustration: trim()
The trim() function removes leading and trailing spaces in a string. NOTE that this function will not remove the inline spaces.
fn main() {
let fullname = " Tutorials Point \r\n";
println!("Before trim ");
println!("length is {}",fullname.len());
println!();
println!("After trim ");
println!("length is {}",fullname.trim().len());
}
Output
The above program generates the following output −
Before trim
length is 24
After trim
length is 15
Illustration:split_whitespace()
The split_whitespace() splits the input string into different strings. It returns an iterator so we are iterating through the tokens as shown below −
fn main(){
let msg = "Tutorials Point has good t
utorials".to_string();
let mut i = 1;
for token in msg.split_whitespace(){
println!("token {} {}",i,token);
i+=1;
}
}
Output
token 1 Tutorials
token 2 Point
token 3 has
token 4 good
token 5 tutorials
Illustration: split() string
The split() string method returns an iterator over substrings of a string slice, separated by characters matched by a pattern. The limitation of the split() method is that the result cannot be stored for later use. The collect method can be used to store the result returned by split() as a vector.
fn main() {
let fullname = "Kannan,Sudhakaran,Tutorialspoint";
for token in fullname.split(","){
println!("token is {}",token);
}
//store in a Vector
println!("\n");
let tokens:Vec<&str>= fullname.split(",").collect();
println!("firstName is {}",tokens[0]);
println!("lastname is {}",tokens[1]);
println!("company is {}",tokens[2]);
}
The above example splits the string fullname, whenever it encounters a comma (,).
Output
token is Kannan
token is Sudhakaran
token is Tutorialspoint
firstName is Kannan
lastname is Sudhakaran
company is Tutorialspoint
Illustration: chars()
Individual characters in a string can be accessed using the chars method. Let us consider an example to understand this.
fn main(){
let n1 = "Tutorials".to_string();
for n in n1.chars(){
println!("{}",n);
}
}
Output
T
u
t
o
r
i
a
l
s
Concatenation of Strings with + operator
A string value can be appended to another string. This is called concatenation or interpolation. The result of string concatenation is a new string object. The + operator internally uses an add method. The syntax of the add function takes two parameters. The first parameter is self – the string object itself and the second parameter is a reference of the second string object. This is shown below −
//add function
add(self,&str)->String {
// returns a String object
}
Illustration: String Concatenation
fn main(){
let n1 = "Tutorials".to_string();
let n2 = "Point".to_string();
let n3 = n1 + &n2; // n2 reference is passed
println!("{}",n3);
}
The Output will be as given below
TutorialsPoint
Illustration: Type Casting
The following example illustrates converting a number to a string object −
fn main(){
let number = 2020;
let number_as_string = number.to_string();
// convert number to string
println!("{}",number_as_string);
println!("{}",number_as_string=="2020");
}
The Output will be as given below
2020
true
Illustration: Format! Macro
Another way to add to String objects together is using a macro function called format. The use of Format! is as shown below.
fn main(){
let n1 = "Tutorials".to_string();
let n2 = "Point".to_string();
let n3 = format!("{} {}",n1,n2);
println!("{}",n3);
}
The Output will be as given below
Tutorials Point
An operator defines some function that will be performed on the data. The data on which operators work are called operands. Consider the following expression −
7 + 5 = 12
Here, the values 7, 5, and 12 are operands, while + and = are operators.
The major operators in Rust can be classified as −
- Arithmetic
- Bitwise
- Comparison
- Logical
- Bitwise
- Conditional
Arithmetic Operators
Assume the values in variables a and b are 10 and 5 respectively.
Show Examples
Sr.No | Operator | Description | Example |
---|---|---|---|
1 | +(Addition) | returns the sum of the operands | a+b is 15 |
2 | -(Subtraction) | returns the difference of the values | a-b is 5 |
3 | * (Multiplication) | returns the product of the values | a*b is 50 |
4 | / (Division) | performs division operation and returns the quotient | a / b is 2 |
5 | % (Modulus) | performs division operation and returns the remainder | a % b is 0 |
NOTE − The ++ and -- operators are not supported in Rust.
Relational Operators
Relational Operators test or define the kind of relationship between two entities. Relational operators are used to compare two or more values. Relational operators return a Boolean value − true or false.
Assume the value of A is 10 and B is 20.
Show Examples
Sr.No | Operator | Description | Example |
---|---|---|---|
1 | > | Greater than | (A > B) is False |
2 | < | Lesser than | (A < B) is True |
3 | >= | Greater than or equal to | (A >= B) is False |
4 | <= | Lesser than or equal to | (A <= B) is True |
5 | == | Equality | (A == B) is fals |
6 | != | Not equal | (A != B) is True |
Logical Operators
Logical Operators are used to combine two or more conditions. Logical operators too, return a Boolean value. Assume the value of variable A is 10 and B is 20.
Show Examples
Sr.No | Operator | Description | Example |
---|---|---|---|
1 | && (And) | The operator returns true only if all the expressions specified return true | (A > 10 && B > 10) is False |
2 | ||(OR) | The operator returns true if at least one of the expressions specified return true | (A > 10 || B >10) is True |
3 | ! (NOT) | The operator returns the inverse of the expression’s result. For E.g.: !(>5) returns false | !(A >10 ) is True |
Bitwise Operators
Assume variable A = 2 and B = 3.
Show Examples
Sr.No | Operator | Description | Example |
---|---|---|---|
1 | & (Bitwise AND) | It performs a Boolean AND operation on each bit of its integer arguments. | (A & B) is 2 |
2 | | (BitWise OR) | It performs a Boolean OR operation on each bit of its integer arguments. | (A | B) is 3 |
3 | ^ (Bitwise XOR) | It performs a Boolean exclusive OR operation on each bit of its integer arguments. Exclusive OR means that either operand one is true or operand two is true, but not both. | (A ^ B) is 1 |
4 | ! (Bitwise Not) | It is a unary operator and operates by reversing all the bits in the operand. | (!B) is -4 |
5 | << (Left Shift) | It moves all the bits in its first operand to the left by the number of places specified in the second operand. New bits are filled with zeros. Shifting a value left by one position is equivalent to multiplying it by 2, shifting two positions is equivalent to multiplying by 4, and so on. | (A << 1) is 4 |
6 | >> (Right Shift) | Binary Right Shift Operator. The left operand’s value is moved right by the number of bits specified by the right operand. | (A >> 1) is 1 |
7 | >>> (Right shift with Zero) | This operator is just like the >> operator, except that the bits shifted to the left are always zero. | (A >>> 1) is 1 |
Decision-making structures require that the programmer specify one or more conditions to be evaluated or tested by the program, along with a statement or statements to be executed if the condition is determined to be true, and optionally, other statements to be executed if the condition is determined to be false.
Shown below is the general form of a typical decision-making structure found in most of the programming languages −
Sr.No | Statement & Description |
---|---|
1 | if statement An if statement consists of a Boolean expression followed by one or more statements. |
2 | if...else statement An if statement can be followed by an optional else statement, which executes when the Boolean expression is false. |
3 | else...if and nested ifstatement You can use one if or else if statement inside another if or else if statement(s). |
4 | match statement A match statement allows a variable to be tested against a list of values. |
If Statement
The if…else construct evaluates a condition before a block of code is executed.
Syntax
if boolean_expression {
// statement(s) will execute if the boolean expression is true
}
If the Boolean expression evaluates to true, then the block of code inside the if statement will be executed. If the Boolean expression evaluates to false, then the first set of code after the end of the if statement (after the closing curly brace) will be executed.
fn main(){
let num:i32 = 5;
if num > 0 {
println!("number is positive") ;
}
}
The above example will print number is positive as the condition specified by the if block is true.
if else statement
An if can be followed by an optional else block. The else block will execute if the Boolean expression tested by the if statement evaluates to false.
Syntax
if boolean_expression {
// statement(s) will execute if the boolean expression is true
} else {
// statement(s) will execute if the boolean expression is false
}
FlowChart
The if block guards the conditional expression. The block associated with the if statement is executed if the Boolean expression evaluates to true.
The if block may be followed by an optional else statement. The instruction block associated with the else block is executed if the expression evaluates to false.
Illustration - Simple if…else
fn main() {
let num = 12;
if num % 2==0 {
println!("Even");
} else {
println!("Odd");
}
}
The above example prints whether the value in a variable is even or odd. The if block checks the divisibility of the value by 2 to determine the same. Here is the output of the above code −
Even
Nested If
The else…if ladder is useful to test multiple conditions. The syntax is as shown below −
Syntax
if boolean_expression1 {
//statements if the expression1 evaluates to true
} else if boolean_expression2 {
//statements if the expression2 evaluates to true
} else {
//statements if both expression1 and expression2 result to false
}
When using if…else…if and else statements, there are a few points to keep in mind.
- An if can have zero or one else's and it must come after any else..if.
- An if can have zero to many else..if and they must come before the else.
- Once an else..if succeeds, none of the remaining else..if or else will be tested.
Example: else…if ladder
fn main() {
let num = 2 ;
if num > 0 {
println!("{} is positive",num);
} else if num < 0 {
println!("{} is negative",num);
} else {
println!("{} is neither positive nor negative",num) ;
}
}
The snippet displays whether the value is positive, negative or zero.
Output
2 is positive
Match Statement
The match statement checks if a current value is matching from a list of values, this is very much similar to the switch statement in C language. In the first place, notice that the expression following the match keyword does not have to be enclosed in parentheses.
The syntax is as shown below.
let expressionResult = match variable_expression {
constant_expr1 => {
//statements;
},
constant_expr2 => {
//statements;
},
_ => {
//default
}
};
In the example given below, state_code is matched with a list of values MH, KL, KA, GA − if any match is found, a string value is returned to variable state. If no match is found, the default case _ matches and value Unkown is returned.
fn main(){
let state_code = "MH";
let state = match state_code {
"MH" => {println!("Found match for MH"); "Maharashtra"},
"KL" => "Kerala",
"KA" => "Karnadaka",
"GA" => "Goa",
_ => "Unknown"
};
println!("State name is {}",state);
}
Output
Found match for MH
State name is Maharashtra
There may be instances, where a block of code needs to be executed repeatedly. In general, programming instructions are executed sequentially: The first statement in a function is executed first, followed by the second, and so on.
Programming languages provide various control structures that allow for more complicated execution paths.
A loop statement allows us to execute a statement or group of statements multiple times. Given below is the general form of a loop statement in most of the programming languages.
Rust provides different types of loops to handle looping requirements −
- while
- loop
- for
Definite Loop
A loop the number of iterations of which is definite/fixed is termed as a definite loop. The for loop is an implementation of a definite loop.
For Loop
The for loop executes the code block for a specified number of times. It can be used to iterate over a fixed set of values, such as an array. The syntax of the for loop is as given below
Syntax
for temp_variable in lower_bound..upper_bound {
//statements
}
An example of a for loop is as shown below
fn main(){
for x in 1..11{ // 11 is not inclusive
if x==5 {
continue;
}
println!("x is {}",x);
}
}
NOTE: that the variable x is only accessible within the for block.
Output
x is 1
x is 2
x is 3
x is 4
x is 6
x is 7
x is 8
x is 9
x is 10
Indefinite Loop
An indefinite loop is used when the number of iterations in a loop is indeterminate or unknown.
Indefinite loops can be implemented using −
Sr.No | Name & Description |
---|---|
1 | While The while loop executes the instructions each time the condition specified evaluates to true |
2 | Loop The loop is a while(true) indefinite loop |
Illustration − for while
fn main(){
let mut x = 0;
while x < 10{
x+=1;
println!("inside loop x value is {}",x);
}
println!("outside loop x value is {}",x);
}
The output is as shown below −
inside loop x value is 1
inside loop x value is 2
inside loop x value is 3
inside loop x value is 4
inside loop x value is 5
inside loop x value is 6
inside loop x value is 7
inside loop x value is 8
inside loop x value is 9
inside loop x value is 10
outside loop x value is 10
Illustration −loop
fn main(){
//while true
let mut x = 0;
loop {
x+=1;
println!("x={}",x);
if x==15 {
break;
}
}
}
ザ・ breakステートメントは、構成から制御を取り除くために使用されます。ループでbreakを使用すると、プログラムはループを終了します。
出力
x=1
x=2
x=3
x=4
x=5
x=6
x=7
x=8
x=9
x=10
x=11
x=12
x=13
x=14
x=15
ステートメントを続ける
continueステートメントは、現在の反復の後続のステートメントをスキップし、制御をループの最初に戻します。breakステートメントとは異なり、continueはループを終了しません。現在の反復を終了し、次の反復を開始します。
継続ステートメントの例を以下に示します。
fn main() {
let mut count = 0;
for num in 0..21 {
if num % 2==0 {
continue;
}
count+=1;
}
println! (" The count of odd values between 0 and 20 is: {} ",count);
//outputs 10
}
上記の例は、0から20までの偶数値の数を表示します。数が偶数の場合、ループは現在の反復を終了します。これは、continueステートメントを使用して実現されます。
0から20までの奇数値の数は10です。
関数は、読み取り可能、保守可能、および再利用可能なコードの構成要素です。関数は、特定のタスクを実行するための一連のステートメントです。関数は、プログラムをコードの論理ブロックに編成します。定義すると、関数を呼び出してコードにアクセスできます。これにより、コードが再利用可能になります。さらに、関数を使用すると、プログラムのコードを簡単に読み取って保守できます。
関数宣言は、関数の名前、戻り値の型、およびパラメーターについてコンパイラーに通知します。関数定義は、関数の実際の本体を提供します。
シニア番号 | 機能と説明 |
---|---|
1 | Defining a function TA関数定義は、特定のタスクが実行される内容と方法を指定します。 |
2 | Calling or invoking a Function 関数を実行するには、関数を呼び出す必要があります。 |
3 | Returning Functions 関数は、制御とともに値を呼び出し元に返すこともできます。 |
4 | Parameterized Function パラメータは、関数に値を渡すメカニズムです。 |
関数の定義
関数定義は、特定のタスクが実行される内容と方法を指定します。関数を使用する前に、関数を定義する必要があります。関数本体には、関数によって実行される必要のあるコードが含まれています。関数に名前を付けるための規則は、変数の規則と似ています。関数は、を使用して定義されますfnキーワード。標準関数を定義するための構文を以下に示します。
構文
fn function_name(param1,param2..paramN) {
// function body
}
関数宣言には、オプションでパラメーター/引数を含めることができます。パラメータは、関数に値を渡すために使用されます。
例-単純な関数定義
//Defining a function
fn fn_hello(){
println!("hello from function fn_hello ");
}
関数の呼び出し
関数を実行するには、関数を呼び出す必要があります。このプロセスは、function invocation。関数を呼び出すときに、パラメーターの値を渡す必要があります。別の関数を呼び出す関数は、caller function.
構文
function_name(val1,val2,valN)
例:関数の呼び出し
fn main(){
//calling a function
fn_hello();
}
ここで、main()は呼び出し元の関数です。
図
次の例では、関数を定義しています fn_hello()。この関数は、コンソールにメッセージを出力します。ザ・main()関数はfn_hello()関数を呼び出します。
fn main(){
//calling a function
fn_hello();
}
//Defining a function
fn fn_hello(){
println!("hello from function fn_hello ");
}
出力
hello from function fn_hello
関数からの戻り値
関数は、制御とともに値を呼び出し元に返すこともあります。このような関数は、戻り関数と呼ばれます。
構文
次の構文のいずれかを使用して、戻り値の型を持つ関数を定義できます。
リターンステートメント付き
// Syntax1
fn function_name() -> return_type {
//statements
return value;
}
returnステートメントのない省略構文
//Syntax2
fn function_name() -> return_type {
value //no semicolon means this value is returned
}
イラスト
fn main(){
println!("pi value is {}",get_pi());
}
fn get_pi()->f64 {
22.0/7.0
}
出力
pi value is 3.142857142857143
パラメータ付き関数
パラメータは、関数に値を渡すメカニズムです。パラメータは、関数のシグネチャの一部を形成します。パラメータ値は、呼び出し中に関数に渡されます。明示的に指定されていない限り、関数に渡される値の数は、定義されたパラメーターの数と一致する必要があります。
次のいずれかの手法を使用して、パラメータを関数に渡すことができます。
値渡し
メソッドが呼び出されると、値パラメーターごとに新しい保管場所が作成されます。実際のパラメータの値がそれらにコピーされます。したがって、呼び出されたメソッド内のパラメーターに加えられた変更は、引数に影響を与えません。
次の例では、最初は5である変数noを宣言しています。変数はパラメーターとして(値によって)に渡されます。 mutate_no_to_zero()値をゼロに変更する関数。関数呼び出し後、コントロールがmainメソッドに戻ると、値は同じになります。
fn main(){
let no:i32 = 5;
mutate_no_to_zero(no);
println!("The value of no is:{}",no);
}
fn mutate_no_to_zero(mut param_no: i32) {
param_no = param_no*0;
println!("param_no value is :{}",param_no);
}
出力
param_no value is :0
The value of no is:5
参照渡し
値パラメーターとは異なり、参照によってパラメーターを渡す場合、これらのパラメーターの新しい保管場所は作成されません。参照パラメータは、メソッドに提供される実際のパラメータと同じメモリ位置を表します。パラメータ値は、変数名の前に& 。
以下に示す例では、可変持たない変数に最初に5 Aの参照がないに渡されています、mutate_no_to_zero()関数。関数は元の変数で動作します。関数呼び出しの後、制御がmainメソッドに戻ると、元の変数の値はゼロになります。
fn main() {
let mut no:i32 = 5;
mutate_no_to_zero(&mut no);
println!("The value of no is:{}",no);
}
fn mutate_no_to_zero(param_no:&mut i32){
*param_no = 0; //de reference
}
*演算子は、変数が格納されているメモリ位置に格納されている値にアクセスするために使用されます param_noに指差す。これは、間接参照とも呼ばれます。
出力は-になります
The value of no is 0.
関数に文字列を渡す
メイン()関数は、に文字列オブジェクトを渡しディスプレイ()関数。
fn main(){
let name:String = String::from("TutorialsPoint");
display(name);
//cannot access name after display
}
fn display(param_name:String){
println!("param_name value is :{}",param_name);
}
出力
param_name value is :TutorialsPoint
タプルは複合データ型です。スカラー型は、1つの型のデータのみを格納できます。たとえば、i32変数は単一の整数値のみを格納できます。複合タイプでは、一度に複数の値を格納でき、さまざまなタイプにすることができます。
タプルの長さは固定されています。宣言すると、サイズを拡大または縮小することはできません。タプルインデックスは0。
構文
//Syntax1
let tuple_name:(data_type1,data_type2,data_type3) = (value1,value2,value3);
//Syntax2
let tuple_name = (value1,value2,value3);
図
次の例では、値をタプルで表示しています。
fn main() {
let tuple:(i32,f64,u8) = (-325,4.9,22);
println!("{:?}",tuple);
}
printlnは!(「{}」、タプル)構文はタプルの値を表示するために使用することができません。これは、タプルが複合タイプであるためです。使用のprintlnを!(「{}:?」、tuple_name)タプルの値を出力する構文。
出力
(-325, 4.9, 22)
図
次の例では、個々の値をタプルで出力します。
fn main() {
let tuple:(i32,f64,u8) = (-325,4.9,22);
println!("integer is :{:?}",tuple.0);
println!("float is :{:?}",tuple.1);
println!("unsigned integer is :{:?}",tuple.2);
}
出力
integer is :-325
float is :4.9
unsigned integer is :2
図
次の例では、タプルをパラメーターとして関数に渡します。タプルは値によって関数に渡されます。
fn main(){
let b:(i32,bool,f64) = (110,true,10.9);
print(b);
}
//pass the tuple as a parameter
fn print(x:(i32,bool,f64)){
println!("Inside print method");
println!("{:?}",x);
}
出力
Inside print method
(110, true, 10.9)
破壊する
破壊的な割り当ては、タプルの値をアンパックする錆の特徴です。これは、タプルを個別の変数に割り当てることによって実現されます。
次の例を考えてみましょう-
fn main(){
let b:(i32,bool,f64) = (30,true,7.9);
print(b);
}
fn print(x:(i32,bool,f64)){
println!("Inside print method");
let (age,is_male,cgpa) = x; //assigns a tuple to
distinct variables
println!("Age is {} , isMale? {},cgpa is
{}",age,is_male,cgpa);
}
変数xは、letステートメントに割り当てられるタプルです。各変数(age、is_male、およびcgpa)には、対応する値がタプルに含まれます。
出力
Inside print method
Age is 30 , isMale? true,cgpa is 7.9
この章では、配列とそれに関連するさまざまな機能について学習します。配列について学ぶ前に、配列が変数とどのように異なるかを見てみましょう。
変数には次の制限があります-
変数は本質的にスカラーです。つまり、変数宣言には一度に1つの値しか含めることができません。これは、n個の値をプログラムに格納するにはn個の変数宣言が必要になることを意味します。したがって、より多くの値のコレクションを格納する必要がある場合、変数の使用は実行できません。
プログラム内の変数にはランダムな順序でメモリが割り当てられるため、宣言された順序で値を取得/読み取ることが困難になります。
配列は、値の同種のコレクションです。簡単に言えば、配列は同じデータ型の値のコレクションです。
アレイの機能
アレイの特徴は以下のとおりです。
配列宣言は、シーケンシャルメモリブロックを割り当てます。
配列は静的です。これは、一度初期化された配列のサイズを変更できないことを意味します。
各メモリブロックは配列要素を表します。
配列要素は、要素の添え字/インデックスと呼ばれる一意の整数によって識別されます。
配列要素への入力は、配列初期化と呼ばれます。
配列要素の値は更新または変更できますが、削除することはできません。
配列の宣言と初期化
以下の構文を使用して、Rustで配列を宣言および初期化します。
構文
//Syntax1
let variable_name = [value1,value2,value3];
//Syntax2
let variable_name:[dataType;size] = [value1,value2,value3];
//Syntax3
let variable_name:[dataType;size] = [default_value_for_elements,size];
最初の構文では、配列のタイプは、初期化中に配列の最初の要素のデータ型から推測されます。
イラスト:単純な配列
次の例では、配列のサイズとデータ型を明示的に指定しています。println!()関数の{:?}構文は、配列内のすべての値を出力するために使用されます。LEN()関数は、配列のサイズを計算するために使用されます。
fn main(){
let arr:[i32;4] = [10,20,30,40];
println!("array is {:?}",arr);
println!("array size is :{}",arr.len());
}
出力
array is [10, 20, 30, 40]
array size is :4
イラスト:データ型のない配列
次のプログラムは、4つの要素の配列を宣言します。データ型は、変数宣言時に明示的に指定されていません。この場合、配列は整数型になります。LEN()関数は、配列のサイズを計算するために使用されます。
fn main(){
let arr = [10,20,30,40];
println!("array is {:?}",arr);
println!("array size is :{}",arr.len());
}
出力
array is [10, 20, 30, 40]
array size is :4
イラスト:デフォルト値
次の例では、配列を作成し、そのすべての要素をデフォルト値-1で初期化します。
fn main() {
let arr:[i32;4] = [-1;4];
println!("array is {:?}",arr);
println!("array size is :{}",arr.len());
}
出力
array is [-1, -1, -1, -1]
array size is :4
イラスト:forループのある配列
次の例では、配列を反復処理し、インデックスとそれに対応する値を出力します。ループは、インデックス0から4(最後の配列要素のインデックス)までの値を取得します。
fn main(){
let arr:[i32;4] = [10,20,30,40];
println!("array is {:?}",arr);
println!("array size is :{}",arr.len());
for index in 0..4 {
println!("index is: {} & value is : {}",index,arr[index]);
}
}
出力
array is [10, 20, 30, 40]
array size is :4
index is: 0 & value is : 10
index is: 1 & value is : 20
index is: 2 & value is : 30
index is: 3 & value is : 40
イラスト:iter()関数の使用
iter()関数は、配列内のすべての要素の値をフェッチします。
fn main(){
let arr:[i32;4] = [10,20,30,40];
println!("array is {:?}",arr);
println!("array size is :{}",arr.len());
for val in arr.iter(){
println!("value is :{}",val);
}
}
出力
array is [10, 20, 30, 40]
array size is :4
value is :10
value is :20
value is :30
value is :40
イラスト:可変配列
MUTのキーワードは、可変配列を宣言するために使用することができます。次の例では、可変配列を宣言し、2番目の配列要素の値を変更します。
fn main(){
let mut arr:[i32;4] = [10,20,30,40];
arr[1] = 0;
println!("{:?}",arr);
}
出力
[10, 0, 30, 40]
関数へのパラメーターとしての配列の受け渡し
配列は、値または関数への参照によって渡すことができます。
イラスト:値渡し
fn main() {
let arr = [10,20,30];
update(arr);
print!("Inside main {:?}",arr);
}
fn update(mut arr:[i32;3]){
for i in 0..3 {
arr[i] = 0;
}
println!("Inside update {:?}",arr);
}
出力
Inside update [0, 0, 0]
Inside main [10, 20, 30]
イラスト:参照渡し
fn main() {
let mut arr = [10,20,30];
update(&mut arr);
print!("Inside main {:?}",arr);
}
fn update(arr:&mut [i32;3]){
for i in 0..3 {
arr[i] = 0;
}
println!("Inside update {:?}",arr);
}
出力
Inside update [0, 0, 0]
Inside main [0, 0, 0]
配列宣言と定数
配列の宣言と定数を理解するために、以下の例を考えてみましょう。
fn main() {
let N: usize = 20;
let arr = [0; N]; //Error: non-constant used with constant
print!("{}",arr[10])
}
コンパイラは例外を発生させます。これは、コンパイル時に配列の長さを知る必要があるためです。ここで、変数「N」の値は実行時に決定されます。つまり、変数を使用して配列のサイズを定義することはできません。
ただし、次のプログラムは有効です-
fn main() {
const N: usize = 20;
// pointer sized
let arr = [0; N];
print!("{}",arr[10])
}
constキーワードのプレフィックスが付いた識別子の値は、コンパイル時に定義され、実行時に変更することはできません。usizeはポインタサイズであるため、実際のサイズはプログラムをコンパイルするアーキテクチャによって異なります。
プログラムのメモリは、次のように割り当てることができます。
- Stack
- Heap
スタック
スタックは後入れ先出し順で続きます。スタックは、コンパイル時にサイズがわかっているデータ値を格納します。たとえば、固定サイズi32の変数は、スタック割り当ての候補です。そのサイズはコンパイル時にわかります。サイズが固定されているため、すべてのスカラー型をスタックに格納できます。
実行時に値が割り当てられる文字列の例を考えてみましょう。このような文字列の正確なサイズは、コンパイル時に決定できません。したがって、スタック割り当ての候補ではなく、ヒープ割り当ての候補です。
ヒープ
ヒープメモリには、コンパイル時にサイズが不明なデータ値が格納されます。動的データを保存するために使用されます。簡単に言うと、ヒープメモリは、プログラムのライフサイクル全体で変化する可能性のあるデータ値に割り当てられます。ヒープは、スタックと比較した場合に整理されていないメモリ内の領域です。
所有権とは何ですか?
Rustの各値には、と呼ばれる変数があります owner値の。Rustに保存されているすべてのデータには、所有者が関連付けられています。たとえば、構文-let age = 30では、ageは値30の所有者です。
各データは、一度に1人の所有者のみを持つことができます。
2つの変数が同じメモリ位置を指すことはできません。変数は常に異なるメモリ位置を指します。
所有権の譲渡
価値の所有権は、次の方法で譲渡できます。
ある変数の値を別の変数に割り当てる。
関数に値を渡す。
関数からの戻り値。
ある変数の値を別の変数に割り当てる
言語としてのRustの主なセールスポイントは、メモリの安全性です。メモリの安全性は、誰がいつ何を使用できるかを厳密に制御することで実現されます。
次のスニペットを検討してください-
fn main(){
let v = vec![1,2,3];
// vector v owns the object in heap
//only a single variable owns the heap memory at any given time
let v2 = v;
// here two variables owns heap value,
//two pointers to the same content is not allowed in rust
//Rust is very smart in terms of memory access ,so it detects a race condition
//as two variables point to same heap
println!("{:?}",v);
}
上記の例では、ベクトルvを宣言しています。所有権の概念は、1つの変数のみがリソースにバインドすることです。 v リソースにバインドするか、 v2リソースにバインドします。上記の例はエラーをスローします-移動された値の使用: `v`。これは、リソースの所有権がv2に譲渡されるためです。これは、所有権がvからv2(v2 = v)に移動され、移動後にvが無効になることを意味します。
関数に値を渡す
ヒープ内のオブジェクトをクロージャまたは関数に渡すと、値の所有権も変更されます。
fn main(){
let v = vec![1,2,3]; // vector v owns the object in heap
let v2 = v; // moves ownership to v2
display(v2); // v2 is moved to display and v2 is invalidated
println!("In main {:?}",v2); //v2 is No longer usable here
}
fn display(v:Vec<i32>){
println!("inside display {:?}",v);
}
関数からの戻り値
関数に渡された所有権は、関数の実行が完了すると無効になります。これを回避する1つの回避策は、関数が所有オブジェクトを呼び出し元に返すようにすることです。
fn main(){
let v = vec![1,2,3]; // vector v owns the object in heap
let v2 = v; // moves ownership to v2
let v2_return = display(v2);
println!("In main {:?}",v2_return);
}
fn display(v:Vec<i32>)->Vec<i32> {
// returning same vector
println!("inside display {:?}",v);
}
所有権とプリミティブ型
プリミティブ型の場合、ある変数の内容が別の変数にコピーされます。したがって、所有権の移動は発生していません。これは、プリミティブ変数が必要とするリソースがオブジェクトよりも少ないためです。次の例を考えてみましょう-
fn main(){
let u1 = 10;
let u2 = u1; // u1 value copied(not moved) to u2
println!("u1 = {}",u1);
}
出力は–10になります。
変数の所有権を別の関数に渡してから所有権を返すのは非常に不便です。Rustは、値の所有権が一時的にエンティティに転送されてから、元の所有者エンティティに戻されるという概念である借用をサポートします。
次のことを考慮してください-
fn main(){
// a list of nos
let v = vec![10,20,30];
print_vector(v);
println!("{}",v[0]); // this line gives error
}
fn print_vector(x:Vec<i32>){
println!("Inside print_vector function {:?}",x);
}
main関数は、関数print_vector()を呼び出します。ベクトルは、パラメーターとしてこの関数に渡されます。ベクターの所有権もに渡されprint_vector()の関数(メイン)。上記のコードは、main()関数がベクトルvにアクセスしようとすると、以下に示すようなエラーになります。
| print_vector(v);
| - value moved here
| println!("{}",v[0]);
| ^ value used here after move
これは、所有権が別の関数に譲渡されると、変数または値を最初に所有していた関数で使用できなくなるためです。
借入とは何ですか?
関数が変数/値に対する制御を一時的に別の関数に移すとき、しばらくの間、それは借用と呼ばれます。これは、変数への参照を渡すことによって実現されます(& var_name)変数/値自体を関数に渡すのではなく。変数/値の所有権は、制御が渡された関数の実行が完了した後、変数の元の所有者に譲渡されます。
fn main(){
// a list of nos
let v = vec![10,20,30];
print_vector(&v); // passing reference
println!("Printing the value from main() v[0]={}",v[0]);
}
fn print_vector(x:&Vec<i32>){
println!("Inside print_vector function {:?}",x);
}
出力
Inside print_vector function [10, 20, 30]
Printing the value from main() v[0] = 10
可変参照
関数は、借用したリソースへの可変参照を使用して、借用したリソースを変更できます。可変参照の接頭辞は&mut。可変参照は、可変変数に対してのみ機能します。
イラスト:整数参照の変更
fn add_one(e: &mut i32) {
*e+= 1;
}
fn main() {
let mut i = 3;
add_one(&mut i);
println!("{}", i);
}
メイン()関数は、変更可能な整数変数宣言のIとに対するIの変更可能な参照を渡しadd_one()。add_one()は、変数iの値を1つインクリメントします。
イラスト:文字列参照の変更
fn main() {
let mut name:String = String::from("TutorialsPoint");
display(&mut name);
//pass a mutable reference of name
println!("The value of name after modification is:{}",name);
}
fn display(param_name:&mut String){
println!("param_name value is :{}",param_name);
param_name.push_str(" Rocks");
//Modify the actual string,name
}
メイン()関数は、変数の変更可能な参照渡し名の表示()関数。表示関数は、元の名前変数に追加の文字列を追加します。
出力
param_name value is :TutorialsPoint
The value of name after modification is:TutorialsPoint Rocks
スライスは、メモリのブロックへのポインタです。スライスを使用して、連続するメモリブロックに格納されているデータの一部にアクセスできます。配列、ベクトル、文字列などのデータ構造で使用できます。スライスは、インデックス番号を使用してデータの一部にアクセスします。スライスのサイズは実行時に決定されます。
スライスは実際のデータへのポインタです。これらは、借用とも呼ばれる関数への参照によって渡されます。
たとえば、スライスを使用して文字列値の一部をフェッチできます。スライスされた文字列は、実際の文字列オブジェクトへのポインタです。したがって、文字列の開始インデックスと終了インデックスを指定する必要があります。インデックスは、配列と同じように0から始まります。
構文
let sliced_value = &data_structure[start_index..end_index]
最小インデックス値は0で、最大インデックス値はデータ構造のサイズです。end_indexは最終文字列に含まれないことに注意してください。
次の図は、9文字のサンプル文字列Tutorialsを示しています。最初の文字のインデックスは0で、最後の文字のインデックスは8です。
次のコードは、文字列から5文字をフェッチします(インデックス4から開始)。
fn main() {
let n1 = "Tutorials".to_string();
println!("length of string is {}",n1.len());
let c1 = &n1[4..9];
// fetches characters at 4,5,6,7, and 8 indexes
println!("{}",c1);
}
出力
length of string is 9
rials
イラスト-整数配列のスライス
main()関数は、5つの要素を持つ配列を宣言します。それはを呼び出しますuse_slice()関数を実行し、3つの要素のスライスを渡します(データ配列を指します)。スライスは参照によって渡されます。use_slice()関数は、スライスの値とその長さを出力します。
fn main(){
let data = [10,20,30,40,50];
use_slice(&data[1..4]);
//this is effectively borrowing elements for a while
}
fn use_slice(slice:&[i32]) {
// is taking a slice or borrowing a part of an array of i32s
println!("length of slice is {:?}",slice.len());
println!("{:?}",slice);
}
出力
length of slice is 3
[20, 30, 40]
可変スライス
ザ・ &mut キーワードを使用して、スライスを変更可能としてマークできます。
fn main(){
let mut data = [10,20,30,40,50];
use_slice(&mut data[1..4]);
// passes references of
20, 30 and 40
println!("{:?}",data);
}
fn use_slice(slice:&mut [i32]) {
println!("length of slice is {:?}",slice.len());
println!("{:?}",slice);
slice[0] = 1010; // replaces 20 with 1010
}
出力
length of slice is 3
[20, 30, 40]
[10, 1010, 30, 40, 50]
上記のコードは、可変スライスをuse_slice()関数に渡します。この関数は、元の配列の2番目の要素を変更します。
配列は、値の同種のコレクションを表すために使用されます。同様に、構造体はRustで使用できる別のユーザー定義データ型であり、別の構造体を含むさまざまなタイプのデータ項目を組み合わせることができます。構造体は、データをキーと値のペアとして定義します。
構文-構造体の宣言
構造体のキーワードは、構造体を宣言するために使用されます。構造体は静的に型指定されるため、構造体のすべてのフィールドをデータ型に関連付ける必要があります。構造体の命名規則と規則は、変数の命名規則と似ています。構造ブロックはセミコロンで終了する必要があります。
struct Name_of_structure {
field1:data_type,
field2:data_type,
field3:data_type
}
構文-構造の初期化
構造体を宣言した後、各フィールドに値を割り当てる必要があります。これは初期化として知られています。
let instance_name = Name_of_structure {
field1:value1,
field2:value2,
field3:value3
};
//NOTE the semicolon
Syntax: Accessing values in a structure
Use the dot notation to access value of a specific field.
instance_name.field1
Illustration
struct Employee {
name:String,
company:String,
age:u32
}
fn main() {
let emp1 = Employee {
company:String::from("TutorialsPoint"),
name:String::from("Mohtashim"),
age:50
};
println!("Name is :{} company is {} age is {}",emp1.name,emp1.company,emp1.age);
}
上記の例では、名前、会社、タイプの年齢の3つのフィールドを持つstructEmployeeを宣言しています。main()は構造を初期化します。printlnを使用しています!構造体で定義されたフィールドの値を出力するマクロ。
出力
Name is :Mohtashim company is TutorialsPoint age is 50
構造体インスタンスの変更
インスタンスを変更するには、インスタンス変数に変更可能のマークを付ける必要があります。次の例では、Employeeという名前の構造を宣言して初期化し、後で年齢フィールドの値を50から40に変更します。
let mut emp1 = Employee {
company:String::from("TutorialsPoint"),
name:String::from("Mohtashim"),
age:50
};
emp1.age = 40;
println!("Name is :{} company is {} age is
{}",emp1.name,emp1.company,emp1.age);
出力
Name is :Mohtashim company is TutorialsPoint age is 40
構造体を関数に渡す
次の例は、structのインスタンスをパラメーターとして渡す方法を示しています。displayメソッドは、Employeeインスタンスをパラメーターとして受け取り、詳細を出力します。
fn display( emp:Employee) {
println!("Name is :{} company is {} age is
{}",emp.name,emp.company,emp.age);
}
これが完全なプログラムです-
//declare a structure
struct Employee {
name:String,
company:String,
age:u32
}
fn main() {
//initialize a structure
let emp1 = Employee {
company:String::from("TutorialsPoint"),
name:String::from("Mohtashim"),
age:50
};
let emp2 = Employee{
company:String::from("TutorialsPoint"),
name:String::from("Kannan"),
age:32
};
//pass emp1 and emp2 to display()
display(emp1);
display(emp2);
}
// fetch values of specific structure fields using the
// operator and print it to the console
fn display( emp:Employee){
println!("Name is :{} company is {} age is
{}",emp.name,emp.company,emp.age);
}
出力
Name is :Mohtashim company is TutorialsPoint age is 50
Name is :Kannan company is TutorialsPoint age is 32
関数から構造体を返す
2人の従業員の年齢を比較して年長の1人を返す関数who_is_elder()を考えてみましょう。
fn who_is_elder (emp1:Employee,emp2:Employee)->Employee {
if emp1.age>emp2.age {
return emp1;
} else {
return emp2;
}
}
これが完全なプログラムです-
fn main() {
//initialize structure
let emp1 = Employee{
company:String::from("TutorialsPoint"),
name:String::from("Mohtashim"),
age:50
};
let emp2 = Employee {
company:String::from("TutorialsPoint"),
name:String::from("Kannan"),
age:32
};
let elder = who_is_elder(emp1,emp2);
println!("elder is:");
//prints details of the elder employee
display(elder);
}
//accepts instances of employee structure and compares their age
fn who_is_elder (emp1:Employee,emp2:Employee)->Employee {
if emp1.age>emp2.age {
return emp1;
} else {
return emp2;
}
}
//display name, comapny and age of the employee
fn display( emp:Employee) {
println!("Name is :{} company is {} age is {}",emp.name,emp.company,emp.age);
}
//declare a structure
struct Employee {
name:String,
company:String,
age:u32
}
出力
elder is:
Name is :Mohtashim company is TutorialsPoint age is 50
構造の方法
メソッドは関数のようなものです。これらは、プログラミング命令の論理グループです。メソッドはで宣言されますfnキーワード。メソッドのスコープは構造ブロック内にあります。
メソッドは構造ブロックの外で宣言されます。ザ・implキーワードは、構造のコンテキスト内でメソッドを定義するために使用されます。メソッドの最初のパラメータは常にself、構造体の呼び出し元のインスタンスを表します。メソッドは、構造体のデータメンバーを操作します。
メソッドを呼び出すには、最初に構造をインスタンス化する必要があります。このメソッドは、構造体のインスタンスを使用して呼び出すことができます。
構文
struct My_struct {}
impl My_struct {
//set the method's context
fn method_name() {
//define a method
}
}
図
次の例では、フィールドが幅と高さの構造体Rectangleを定義しています。メソッド領域は、構造体のコンテキスト内で定義されます。areaメソッドは、selfキーワードを介して構造体のフィールドにアクセスし、長方形の面積を計算します。
//define dimensions of a rectangle
struct Rectangle {
width:u32, height:u32
}
//logic to calculate area of a rectangle
impl Rectangle {
fn area(&self)->u32 {
//use the . operator to fetch the value of a field via the self keyword
self.width * self.height
}
}
fn main() {
// instanatiate the structure
let small = Rectangle {
width:10,
height:20
};
//print the rectangle's area
println!("width is {} height is {} area of Rectangle
is {}",small.width,small.height,small.area());
}
出力
width is 10 height is 20 area of Rectangle is 200
構造における静的メソッド
静的メソッドは、ユーティリティメソッドとして使用できます。これらのメソッドは、構造がインスタンス化される前でも存在します。静的メソッドは構造体の名前を使用して呼び出され、インスタンスなしでアクセスできます。通常のメソッドとは異なり、静的メソッドは&selfパラメーターを取りません。
構文-静的メソッドの宣言
関数や他のメソッドのような静的メソッドには、オプションでパラメーターを含めることができます。
impl Structure_Name {
//static method that creates objects of the Point structure
fn method_name(param1: datatype, param2: datatype) -> return_type {
// logic goes here
}
}
構文-静的メソッドの呼び出し
構造体名::構文は、静的メソッドにアクセスするために使用されます。
structure_name::method_name(v1,v2)
図
次の例では、構造体Pointのインスタンスを作成して返すファクトリクラスとしてgetInstanceメソッドを使用しています。
//declare a structure
struct Point {
x: i32,
y: i32,
}
impl Point {
//static method that creates objects of the Point structure
fn getInstance(x: i32, y: i32) -> Point {
Point { x: x, y: y }
}
//display values of the structure's field
fn display(&self){
println!("x ={} y={}",self.x,self.y );
}
}
fn main(){
// Invoke the static method
let p1 = Point::getInstance(10,20);
p1.display();
}
出力
x =10 y=20
Rustプログラミングでは、可能なバリアントのリストから値を選択する必要がある場合、列挙データ型を使用します。列挙型は、enumキーワードを使用して宣言されます。以下は列挙型の構文です-
enum enum_name {
variant1,
variant2,
variant3
}
イラスト:列挙型の使用
この例では、列挙型-GenderCategoryを宣言しています。これには、男性と女性のバリアントがあります。プリント!マクロは列挙型の値を表示します。コンパイラは、トレイトstd :: fmt :: DebugがGenderCategoryに実装されていないというエラーをスローします。属性#[derive(Debug)]は、このエラーを抑制するために使用されます。
// The `derive` attribute automatically creates the implementation
// required to make this `enum` printable with `fmt::Debug`.
#[derive(Debug)]
enum GenderCategory {
Male,Female
}
fn main() {
let male = GenderCategory::Male;
let female = GenderCategory::Female;
println!("{:?}",male);
println!("{:?}",female);
}
出力
Male
Female
構造体と列挙型
次の例では、構造Personを定義しています。フィールドgenderは、タイプGenderCategory(列挙型)であり、値として男性または女性のいずれかに割り当てることができます。
// The `derive` attribute automatically creates the
implementation
// required to make this `enum` printable with
`fmt::Debug`.
#[derive(Debug)]
enum GenderCategory {
Male,Female
}
// The `derive` attribute automatically creates the implementation
// required to make this `struct` printable with `fmt::Debug`.
#[derive(Debug)]
struct Person {
name:String,
gender:GenderCategory
}
fn main() {
let p1 = Person {
name:String::from("Mohtashim"),
gender:GenderCategory::Male
};
let p2 = Person {
name:String::from("Amy"),
gender:GenderCategory::Female
};
println!("{:?}",p1);
println!("{:?}",p2);
}
この例では、Personタイプのオブジェクトp1とp2を作成し、これらの各オブジェクトの属性、名前、性別を初期化します。
出力
Person { name: "Mohtashim", gender: Male }
Person { name: "Amy", gender: Female }
オプション列挙型
Optionは、Rust標準ライブラリで事前定義された列挙型です。この列挙型には、Some(data)とNoneの2つの値があります。
構文
enum Option<T> {
Some(T), //used to return a value
None // used to return null, as Rust doesn't support
the null keyword
}
ここで、タイプTは任意のタイプの値を表します。
Rustはnullキーワードをサポートしていません。enumOptionの値Noneは、関数がnull値を返すために使用できます。返すデータがある場合、関数はSome(data)を返すことができます。
例を挙げてこれを理解しましょう-
プログラムは、戻り値の型Optionを持つ関数is_even()を定義します。この関数は、渡された値が偶数であるかどうかを確認します。入力が偶数の場合は値trueが返され、そうでない場合は関数はNoneを返します。
fn main() {
let result = is_even(3);
println!("{:?}",result);
println!("{:?}",is_even(30));
}
fn is_even(no:i32)->Option<bool> {
if no%2 == 0 {
Some(true)
} else {
None
}
}
出力
None
Some(true)
一致ステートメントと列挙型
マッチステートメントは、列挙型に格納された値を比較するために使用することができます。次の例では、関数定義print_sizeかかり、CarTypeのパラメータとして列挙します。この関数は、パラメーター値を事前定義された定数のセットと比較し、適切なメッセージを表示します。
enum CarType {
Hatch,
Sedan,
SUV
}
fn print_size(car:CarType) {
match car {
CarType::Hatch => {
println!("Small sized car");
},
CarType::Sedan => {
println!("medium sized car");
},
CarType::SUV =>{
println!("Large sized Sports Utility car");
}
}
}
fn main(){
print_size(CarType::SUV);
print_size(CarType::Hatch);
print_size(CarType::Sedan);
}
出力
Large sized Sports Utility car
Small sized car
medium sized car
オプションと一致
オプションタイプを返すis_even関数の例は、以下に示すようにmatchステートメントを使用して実装することもできます。
fn main() {
match is_even(5) {
Some(data) => {
if data==true {
println!("Even no");
}
},
None => {
println!("not even");
}
}
}
fn is_even(no:i32)->Option<bool> {
if no%2 == 0 {
Some(true)
} else {
None
}
}
出力
not even
データ型との一致と列挙
列挙型の各バリアントにデータ型を追加することができます。次の例では、列挙型のNameバリアントとUsr_IDバリアントは、それぞれ文字列型と整数型です。次の例は、データ型を持つ列挙型でのmatchステートメントの使用を示しています。
// The `derive` attribute automatically creates the implementation
// required to make this `enum` printable with `fmt::Debug`.
#[derive(Debug)]
enum GenderCategory {
Name(String),Usr_ID(i32)
}
fn main() {
let p1 = GenderCategory::Name(String::from("Mohtashim"));
let p2 = GenderCategory::Usr_ID(100);
println!("{:?}",p1);
println!("{:?}",p2);
match p1 {
GenderCategory::Name(val)=> {
println!("{}",val);
}
GenderCategory::Usr_ID(val)=> {
println!("{}",val);
}
}
}
出力
Name("Mohtashim")
Usr_ID(100)
Mohtashim
コードの論理グループはモジュールと呼ばれます。複数のモジュールがと呼ばれるユニットにコンパイルされますcrate。Rustプログラムには、バイナリクレートまたはライブラリクレートが含まれている場合があります。バイナリクレートは、main()メソッドを持つ実行可能プロジェクトです。ライブラリクレートは、他のプロジェクトで再利用できるコンポーネントのグループです。バイナリクレートとは異なり、ライブラリクレートにはエントリポイント(main()メソッド)がありません。貨物ツールは、Rustの箱を管理するために使用されます。たとえば、ネットワークモジュールにはネットワーク関連の機能が含まれ、グラフィックモジュールには図面関連の機能が含まれます。モジュールは、他のプログラミング言語の名前空間に似ています。サードパーティのクレートは、crates.ioからの貨物を使用してダウンロードできます。
シニア番号 | 用語と説明 |
---|---|
1 | crate Rustのコンパイルユニットです。クレートはバイナリまたはライブラリにコンパイルされます。 |
2 | cargo 木枠用の公式Rustパッケージ管理ツール。 |
3 | module クレート内のコードを論理的にグループ化します。 |
4 | crates.io 公式のRustパッケージレジストリ。 |
構文
//public module
pub mod a_public_module {
pub fn a_public_function() {
//public function
}
fn a_private_function() {
//private function
}
}
//private module
mod a_private_module {
fn a_private_function() {
}
}
モジュールはパブリックまたはプライベートにすることができます。プライベートモジュール内のコンポーネントには、他のモジュールからアクセスできません。Rustのモジュールはデフォルトでプライベートです。それどころか、パブリックモジュールの関数は他のモジュールからアクセスできます。モジュールには接頭辞を付ける必要がありますpub公開するキーワード。パブリックモジュール内の関数もパブリックにする必要があります。
イラスト:モジュールの定義
例では、パブリックモジュールを定義する-映画を。このモジュールには、パラメーターを受け取り、その値を出力する関数play()が含まれています。
pub mod movies {
pub fn play(name:String) {
println!("Playing movie {}",name);
}
}
fn main(){
movies::play("Herold and Kumar".to_string());
}
出力
Playing movie Herold and Kumar
キーワードを使用する
使用キーワードは、パブリックモジュールをインポートすることができます。
構文
use public_module_name::function_name;
図
pub mod movies {
pub fn play(name:String) {
println!("Playing movie {}",name);
}
}
use movies::play;
fn main(){
play("Herold and Kumar ".to_string());
}
出力
Playing movie Herold and Kumar
ネストされたモジュール
モジュールはネストすることもできます。コメディモジュールを内にネストされた英語の更なるにネストされているモジュール、映画のモジュール。以下の例では、movies / english / comedyモジュール内での関数再生を定義しています。
pub mod movies {
pub mod english {
pub mod comedy {
pub fn play(name:String) {
println!("Playing comedy movie {}",name);
}
}
}
}
use movies::english::comedy::play;
// importing a public module
fn main() {
// short path syntax
play("Herold and Kumar".to_string());
play("The Hangover".to_string());
//full path syntax
movies::english::comedy::play("Airplane!".to_string());
}
出力
Playing comedy movie Herold and Kumar
Playing comedy movie The Hangover
Playing comedy movie Airplane!
イラスト-ライブラリクレートを作成し、バイナリクレートで消費する
名前の付いたライブラリクレートを作成しましょう movie_lib、モジュールが含まれています movies。を構築するにはmovie_lib ライブラリクレート、ツールを使用します cargo。
ステップ1-プロジェクトフォルダを作成する
フォルダーmovie-appを作成し、続いてサブフォルダーmovie-libを作成します。フォルダとサブフォルダを作成したら、srcフォルダとこのディレクトリのCargo.tomlファイル。ソースコードはsrcフォルダにあるはずです。srcフォルダーにlib.rsファイルとmovies.rsファイルを作成します。Cargo.tomlのファイルは、バージョン番号、著者名などのようなプロジェクトのメタデータが含まれています
プロジェクトのディレクトリ構造は以下のようになります-
movie-app
movie-lib/
-->Cargo.toml
-->src/
lib.rs
movies.rs
ステップ2-Cargo.tomlファイルを編集してプロジェクトメタデータを追加します
[package]
name = "movies_lib"
version = "0.1.0"
authors = ["Mohtashim"]
ステップ3-lib.rsファイルを編集します。
このファイルに次のモジュール定義を追加します。
pub mod movies;
上記の行はパブリックモジュールを作成します- movies。
ステップ4-movies.rsファイルを編集する
このファイルは、moviesモジュールのすべての機能を定義します。
pub fn play(name:String){
println!("Playing movie {} :movies-app",name);
}
上記のコードは関数を定義します play() パラメータを受け取り、それをコンソールに出力します。
ステップ5-ライブラリクレートを作成する
を使用してアプリをビルドする cargo buildライブラリクレートが適切に構成されているかどうかを確認するコマンド。プロジェクトのルート、つまりmovie-appフォルダーにいることを確認してください。ビルドが成功すると、ターミナルに次のメッセージが表示されます。
D:\Rust\movie-lib> cargo build
Compiling movies_lib v0.1.0 (file:///D:/Rust/movie-lib)
Finished dev [unoptimized + debuginfo] target(s) in 0.67s
ステップ6-テストアプリケーションを作成する
別のフォルダを作成する movie-lib-testmovie-appフォルダーにあり、Cargo.tomlファイルとsrcフォルダーが続きます。これはバイナリクレートであるため、このプロジェクトにはmainメソッドが必要です。これは、以前に作成されたライブラリクレートを消費します。srcフォルダーにmain.rsファイルを作成します。フォルダ構造は次のようになります。
movie-app
movie-lib
// already completed
movie-lib-test/
-->Cargo.toml
-->src/
main.rs
ステップ7-Cargo.tomlファイルに以下を追加します
[package]
name = "test_for_movie_lib"
version = "0.1.0"
authors = ["Mohtashim"]
[dependencies]
movies_lib = { path = "../movie-lib" }
NOTE−ライブラリフォルダへのパスが依存関係として設定されます。次の図は、両方のプロジェクトの内容を示しています。
ステップ8-main.rsファイルに以下を追加します
extern crate movies_lib;
use movies_lib::movies::play;
fn main() {
println!("inside main of test ");
play("Tutorialspoint".to_string())
}
上記のコードは、movies_libという外部パッケージをインポートします。現在のプロジェクトのCargo.tomlをチェックして、クレート名を確認します。
ステップ9-カーゴビルドとカーゴランの使用
以下に示すように、カーゴビルドとカーゴランを使用してバイナリプロジェクトをビルドし、実行します。
Rustの標準コレクションライブラリは、最も一般的な汎用プログラミングデータ構造の効率的な実装を提供します。この章では、一般的に使用されるコレクション(Vector、HashMap、HashSet)の実装について説明します。
ベクター
ベクトルはサイズ変更可能な配列です。連続するメモリブロックに値を格納します。事前定義された構造Vecを使用して、ベクトルを作成できます。ベクトルのいくつかの重要な機能は次のとおりです。
ベクターは実行時に拡大または縮小できます。
ベクターは同種のコレクションです。
ベクトルは、データを特定の順序で要素のシーケンスとして格納します。ベクター内のすべての要素には、一意のインデックス番号が割り当てられます。インデックスは0から始まり、n-1まで上がります。ここで、nはコレクションのサイズです。たとえば、5つの要素のコレクションでは、最初の要素はインデックス0にあり、最後の要素はインデックス4にあります。
ベクトルは、値を末尾(または末尾近く)にのみ追加します。言い換えると、Vectorを使用してスタックを実装できます。
ベクトルのメモリはヒープに割り当てられます。
構文-ベクトルの作成
let mut instance_name = Vec::new();
Vec構造体の静的メソッドnew()は、ベクトルインスタンスを作成するために使用されます。
または、vecを使用してベクトルを作成することもできます。大きい。構文は次のとおりです-
let vector_name = vec![val1,val2,val3]
次の表に、Vec構造の一般的に使用される関数をいくつか示します。
シニア番号 | 方法 | 署名と説明 |
---|---|---|
1 | 新着() | pub fn new()->Vect 新しい空のVecを構築します。ベクトルは、要素がプッシュされるまで割り当てられません。 |
2 | 押す() | pub fn push(&mut self, value: T) コレクションの後ろに要素を追加します。 |
3 | 削除する() | pub fn remove(&mut self, index: usize) -> T ベクトル内の位置インデックスにある要素を削除して返し、その後のすべての要素を左にシフトします。 |
4 | contains() | pub fn contains(&self, x: &T) -> bool スライスに指定された値の要素が含まれている場合はtrueを返します。 |
5 | len() | pub fn len(&self) -> usize ベクトル内の要素の数を返します。これは「長さ」とも呼ばれます。 |
イラスト:ベクトルの作成-new()
ベクトルを作成するには、静的メソッドnew −を使用します。
fn main() {
let mut v = Vec::new();
v.push(20);
v.push(30);
v.push(40);
println!("size of vector is :{}",v.len());
println!("{:?}",v);
}
上記の例では、構造体Vecで定義されている静的メソッドnew()を使用してVectorを作成します。プッシュ(ヴァル)関数は、コレクションにパラメータとして渡された値を追加します。len()関数は、ベクトルの長さを返します。
出力
size of vector is :3
[20, 30, 40]
イラスト:ベクトルの作成-vec!大きい
次のコードは、vecを使用してベクトルを作成します。大きい。ベクトルのデータ型は、それに割り当てられた最初の値が推測されます。
fn main() {
let v = vec![1,2,3];
println!("{:?}",v);
}
出力
[1, 2, 3]
前述のように、ベクトルには同じデータ型の値のみを含めることができます。次のスニペットはエラー[E0308]をスローします:タイプの不一致エラー。
fn main() {
let v = vec![1,2,3,"hello"];
println!("{:?}",v);
}
イラスト:push()
コレクションの最後に要素を追加します。
fn main() {
let mut v = Vec::new();
v.push(20);
v.push(30);
v.push(40);
println!("{:?}",v);
}
出力
[20, 30, 40]
イラスト:remove()
ベクトル内の位置インデックスにある要素を削除して返し、その後のすべての要素を左にシフトします。
fn main() {
let mut v = vec![10,20,30];
v.remove(1);
println!("{:?}",v);
}
出力
[10, 30]
イラスト-contains()
スライスに指定された値の要素が含まれている場合はtrueを返します-
fn main() {
let v = vec![10,20,30];
if v.contains(&10) {
println!("found 10");
}
println!("{:?}",v);
}
出力
found 10
[10, 20, 30]
イラスト:len()
ベクトル内の要素の数を返します。これは「長さ」とも呼ばれます。
fn main() {
let v = vec![1,2,3];
println!("size of vector is :{}",v.len());
}
出力
size of vector is :3
ベクトルから値にアクセスする
ベクトル内の個々の要素には、対応するインデックス番号を使用してアクセスできます。次の例では、最初の要素の値を印刷するベクター広告を作成します。
fn main() {
let mut v = Vec::new();
v.push(20);
v.push(30);
println!("{:?}",v[0]);
}
Output: `20`
ベクトルの値は、コレクションへの参照を使用してフェッチすることもできます。
fn main() {
let mut v = Vec::new();
v.push(20);
v.push(30);
v.push(40);
v.push(500);
for i in &v {
println!("{}",i);
}
println!("{:?}",v);
}
出力
20
30
40
500
[20, 30, 40, 500]
HashMap
マップは、キーと値のペア(エントリと呼ばれる)のコレクションです。マップ内の2つのエントリが同じキーを持つことはできません。つまり、マップはルックアップテーブルです。HashMapは、キーと値をハッシュテーブルに格納します。エントリは任意の順序で保存されます。キーは、HashMapで値を検索するために使用されます。HashMap構造は、std::collectionsモジュール。HashMap構造にアクセスするには、このモジュールを明示的にインポートする必要があります。
構文:HashMapの作成
let mut instance_name = HashMap::new();
HashMap構造体の静的メソッドnew()は、HashMapオブジェクトを作成するために使用されます。このメソッドは空のHashMapを作成します。
HashMapの一般的に使用される関数について以下で説明します-
シニア番号 | 方法 | 署名と説明 |
---|---|---|
1 | インサート() | pub fn insert(&mut self, k: K, v: V) -> Option キーと値のペアを挿入します。キーがない場合はNoneが返されます。更新後、古い値が返されます。 |
2 | len() | pub fn len(&self) -> usize マップ内の要素の数を返します。 |
3 | 取得する() | pub fn get<Q: ?Sized>(&lself, k: &Q) -> Option<&V> where K:Borrow Q:Hash+ Eq キーに対応する値への参照を返します。 |
4 | iter() | pub fn iter(&self) -> Iter<K, V> すべてのキーと値のペアを任意の順序で訪問するイテレータ。イテレータ要素のタイプは(& 'a K、&' a V)です。 |
5 | contains_key | pub fn contains_key<Q: ?Sized>(&self, k: &Q) -> bool マップに指定されたキーの値が含まれている場合はtrueを返します。 |
6 | 削除する() | pub fn remove_entry<Q: ?Sized>(&mut self, k: &Q) -> Option<(K, V)> マップからキーを削除し、キーが以前にマップにあった場合は、保存されているキーと値を返します。 |
イラスト:insert()
キーと値のペアをHashMapに挿入します。
use std::collections::HashMap;
fn main(){
let mut stateCodes = HashMap::new();
stateCodes.insert("KL","Kerala");
stateCodes.insert("MH","Maharashtra");
println!("{:?}",stateCodes);
}
上記のプログラムはHashMapを作成し、2つのキーと値のペアで初期化します。
出力
{"KL": "Kerala", "MH": "Maharashtra"}
イラスト:len()
マップ内の要素の数を返します
use std::collections::HashMap;
fn main() {
let mut stateCodes = HashMap::new();
stateCodes.insert("KL","Kerala");
stateCodes.insert("MH","Maharashtra");
println!("size of map is {}",stateCodes.len());
}
上記の例では、HashMapを作成し、その中の要素の総数を出力します。
出力
size of map is 2
イラスト-get()
キーに対応する値への参照を返します。次の例では、HashMapのキーKLの値を取得します。
use std::collections::HashMap;
fn main() {
let mut stateCodes = HashMap::new();
stateCodes.insert("KL","Kerala");
stateCodes.insert("MH","Maharashtra");
println!("size of map is {}",stateCodes.len());
println!("{:?}",stateCodes);
match stateCodes.get(&"KL") {
Some(value)=> {
println!("Value for key KL is {}",value);
}
None => {
println!("nothing found");
}
}
}
出力
size of map is 2
{"KL": "Kerala", "MH": "Maharashtra"}
Value for key KL is Kerala
イラスト-iter()
すべてのキーと値のペアへの参照を任意の順序で含むイテレータを返します。
use std::collections::HashMap;
fn main() {
let mut stateCodes = HashMap::new();
stateCodes.insert("KL","Kerala");
stateCodes.insert("MH","Maharashtra");
for (key, val) in stateCodes.iter() {
println!("key: {} val: {}", key, val);
}
}
出力
key: MH val: Maharashtra
key: KL val: Kerala
イラスト:contains_key()
マップに指定されたキーの値が含まれている場合はtrueを返します。
use std::collections::HashMap;
fn main() {
let mut stateCodes = HashMap::new();
stateCodes.insert("KL","Kerala");
stateCodes.insert("MH","Maharashtra");
stateCodes.insert("GJ","Gujarat");
if stateCodes.contains_key(&"GJ") {
println!("found key");
}
}
出力
found key
イラスト:remove()
マップからキーを削除します。
use std::collections::HashMap;
fn main() {
let mut stateCodes = HashMap::new();
stateCodes.insert("KL","Kerala");
stateCodes.insert("MH","Maharashtra");
stateCodes.insert("GJ","Gujarat");
println!("length of the hashmap {}",stateCodes.len());
stateCodes.remove(&"GJ");
println!("length of the hashmap after remove() {}",stateCodes.len());
}
出力
length of the hashmap 3
length of the hashmap after remove() 2
HashSet
HashSetは、タイプTの一意の値のセットです。値の追加と削除は高速であり、特定の値がセットに含まれているかどうかをすばやく確認できます。HashSet構造体は、std :: collectionsモジュールで定義されています。このモジュールは、HashSet構造にアクセスするために明示的にインポートする必要があります。
構文:HashSetの作成
let mut hash_set_name = HashSet::new();
HashSet構造体の静的メソッドnewは、HashSetを作成するために使用されます。このメソッドは、空のHashSetを作成します。
次の表に、HashSet構造体で一般的に使用されるメソッドの一部を示します。
シニア番号 | 方法 | 署名と説明 |
---|---|---|
1 | インサート() | pub fn insert(&mut self, value: T) -> bool セットに値を追加します。セットにこの値が存在しない場合はtrueが返され、そうでない場合はfalseが返されます。 |
2 | len() | pub fn len(&self) -> usize セット内の要素の数を返します。 |
3 | 取得する() | pub fn get<Q:?Sized>(&self, value: &Q) -> Option<&T> where T: Borrow,Q: Hash + Eq, 指定された値と等しい値がある場合は、セット内の値への参照を返します。 |
4 | iter() | pub fn iter(&self) -> Iter 任意の順序ですべての要素にアクセスするイテレータを返します。イテレータ要素のタイプは& 'aTです。 |
5 | contains_key | pub fn contains<Q: ?Sized>(&self, value: &Q) -> bool セットに値が含まれている場合はtrueを返します。 |
6 | 削除する() | pub fn remove<Q: ?Sized>(&mut self, value: &Q) -> bool セットから値を削除します。値がセットに存在する場合はtrueを返します。 |
イラスト-insert()
セットに値を追加します。HashSetは、コレクションに重複する値を追加しません。
use std::collections::HashSet;
fn main() {
let mut names = HashSet::new();
names.insert("Mohtashim");
names.insert("Kannan");
names.insert("TutorialsPoint");
names.insert("Mohtashim");//duplicates not added
println!("{:?}",names);
}
出力
{"TutorialsPoint", "Kannan", "Mohtashim"}
イラスト:len()
セット内の要素の数を返します。
use std::collections::HashSet;
fn main() {
let mut names = HashSet::new();
names.insert("Mohtashim");
names.insert("Kannan");
names.insert("TutorialsPoint");
println!("size of the set is {}",names.len());
}
出力
size of the set is 3
イラスト-iter()
任意の順序ですべての要素にアクセスするイテレータを返します。
use std::collections::HashSet;
fn main() {
let mut names = HashSet::new();
names.insert("Mohtashim");
names.insert("Kannan");
names.insert("TutorialsPoint");
names.insert("Mohtashim");
for name in names.iter() {
println!("{}",name);
}
}
出力
TutorialsPoint
Mohtashim
Kannan
イラスト:get()
セット内の値(存在する場合)への参照を返します。これは、指定された値と同じです。
use std::collections::HashSet;
fn main() {
let mut names = HashSet::new();
names.insert("Mohtashim");
names.insert("Kannan");
names.insert("TutorialsPoint");
names.insert("Mohtashim");
match names.get(&"Mohtashim"){
Some(value)=>{
println!("found {}",value);
}
None =>{
println!("not found");
}
}
println!("{:?}",names);
}
出力
found Mohtashim
{"Kannan", "Mohtashim", "TutorialsPoint"}
イラスト-contains()
セットに値が含まれている場合はtrueを返します。
use std::collections::HashSet;
fn main() {
let mut names = HashSet::new();
names.insert("Mohtashim");
names.insert("Kannan");
names.insert("TutorialsPoint");
if names.contains(&"Kannan") {
println!("found name");
}
}
出力
found name
イラスト:remove()
セットから値を削除します。
use std::collections::HashSet;
fn main() {
let mut names = HashSet::new();
names.insert("Mohtashim");
names.insert("Kannan");
names.insert("TutorialsPoint");
println!("length of the Hashset: {}",names.len());
names.remove(&"Kannan");
println!("length of the Hashset after remove() : {}",names.len());
}
出力
length of the Hashset: 3
length of the Hashset after remove() : 2
Rustでは、エラーは次の表に示すように2つの主要なカテゴリに分類できます。
シニア番号 | 名前と説明 | 使用法 |
---|---|---|
1 | Recoverable 処理できるエラー |
結果の列挙型 |
2 | UnRecoverable 処理できないエラー |
パニックマクロ |
回復可能なエラーは、修正可能なエラーです。プログラムは、失敗した操作を再試行するか、回復可能なエラーが発生したときに別のアクションを指定できます。回復可能なエラーによってプログラムが突然失敗することはありません。回復可能なエラーの例は、ファイルが見つかりませんエラーです。
回復不能なエラーが発生すると、プログラムが突然失敗します。回復不能なエラーが発生した場合、プログラムは通常の状態に戻ることはできません。失敗した操作を再試行したり、エラーを元に戻したりすることはできません。回復不能なエラーの例は、配列の終わりを超えた場所にアクセスしようとすることです。
他のプログラミング言語とは異なり、Rustには例外がありません。回復可能なエラーの列挙型Result <T、E>を返し、panicプログラムで回復不能なエラーが発生した場合はマクロ。パニックマクロが突然終了するプログラムを引き起こします。
パニックマクロと回復不能なエラー
パニック!マクロを使用すると、プログラムをすぐに終了して、プログラムの呼び出し元にフィードバックを提供できます。プログラムが回復不能な状態に達したときに使用する必要があります。
fn main() {
panic!("Hello");
println!("End of main"); //unreachable statement
}
上記の例では、プログラムはパニックに遭遇するとすぐに終了します!大きい。
出力
thread 'main' panicked at 'Hello', main.rs:3
イラスト:パニック!大きい
fn main() {
let a = [10,20,30];
a[10]; //invokes a panic since index 10 cannot be reached
}
出力は以下のようになります−
warning: this expression will panic at run-time
--> main.rs:4:4
|
4 | a[10];
| ^^^^^ index out of bounds: the len is 3 but the index is 10
$main
thread 'main' panicked at 'index out of bounds: the len
is 3 but the index is 10', main.rs:4
note: Run with `RUST_BACKTRACE=1` for a backtrace.
プログラムはパニックを引き起こす可能性があります!以下の例に示すように、ビジネスルールに違反した場合のマクロ-
fn main() {
let no = 13;
//try with odd and even
if no%2 == 0 {
println!("Thank you , number is even");
} else {
panic!("NOT_AN_EVEN");
}
println!("End of main");
}
上記の例では、変数に割り当てられた値が奇数の場合にエラーが返されます。
出力
thread 'main' panicked at 'NOT_AN_EVEN', main.rs:9
note: Run with `RUST_BACKTRACE=1` for a backtrace.
結果の列挙型と回復可能なエラー
列挙結果– <T、E>を使用して、回復可能なエラーを処理できます。2つのバリエーションがあります-OK そして Err。 T そして E ジェネリック型パラメーターです。 T OKバリアント内で成功した場合に返される値のタイプを表します。 E Errバリアント内の失敗の場合に返されるエラーのタイプを表します。
enum Result<T,E> {
OK(T),
Err(E)
}
例を使ってこれを理解しましょう-
use std::fs::File;
fn main() {
let f = File::open("main.jpg");
//this file does not exist
println!("{:?}",f);
}
プログラムは、ファイルがすでに存在する場合はOK(File)を返し、ファイルが見つからない場合はErr(Error)を返します。
Err(Error { repr: Os { code: 2, message: "No such file or directory" } })
Errバリアントの処理方法を見てみましょう。
次の例では、を使用してファイルを開くときに返されるエラーを処理します。 match ステートメント
use std::fs::File;
fn main() {
let f = File::open("main.jpg"); // main.jpg doesn't exist
match f {
Ok(f)=> {
println!("file found {:?}",f);
},
Err(e)=> {
println!("file not found \n{:?}",e); //handled error
}
}
println!("end of main");
}
NOTE−ファイルが見つからなかったにもかかわらず、プログラムはメインイベントの終了を出力します。これは、プログラムがエラーを適切に処理したことを意味します。
出力
file not found
Os { code: 2, kind: NotFound, message: "The system cannot find the file specified." }
end of main
図
is_even数が偶数でない場合、関数はエラーを返します。main()関数はこのエラーを処理します。
fn main(){
let result = is_even(13);
match result {
Ok(d)=>{
println!("no is even {}",d);
},
Err(msg)=>{
println!("Error msg is {}",msg);
}
}
println!("end of main");
}
fn is_even(no:i32)->Result<bool,String> {
if no%2==0 {
return Ok(true);
} else {
return Err("NOT_AN_EVEN".to_string());
}
}
NOTE− main関数はエラーを適切に処理するため、mainステートメントの終わりが出力されます。
出力
Error msg is NOT_AN_EVEN
end of main
unwrap()およびexpect()
標準ライブラリには、列挙型-結果<T、E>とオプション<T>の両方が実装するヘルパーメソッドがいくつか含まれています。それらを使用して、実際に失敗することを期待しないエラーケースを単純化できます。メソッドから成功した場合、「アンラップ」関数を使用して実際の結果を抽出します。
シニア番号 | 方法 | 署名と説明 |
---|---|---|
1 | アンラップ | unwrap(self): T 自己がOk / Someであることを期待し、中に含まれる値を返します。もしそれがErr または None 代わりに、エラーの内容が表示されてパニックが発生します。 |
2 | 期待する | expect(self, msg: &str): T エラーの内容に加えて、パニックになる前にカスタムメッセージを出力することを除いて、unwrapのように動作します。 |
unwrap()
unwrap()関数は、操作が成功した実際の結果を返します。操作が失敗すると、デフォルトのエラーメッセージとともにパニックが返されます。この関数は、matchステートメントの省略形です。これを以下の例に示します-
fn main(){
let result = is_even(10).unwrap();
println!("result is {}",result);
println!("end of main");
}
fn is_even(no:i32)->Result<bool,String> {
if no%2==0 {
return Ok(true);
} else {
return Err("NOT_AN_EVEN".to_string());
}
}
result is true
end of main
上記のコードを変更して、奇数を is_even() 関数。
アンラップ()関数は、パニックと以下のようにデフォルトのエラーメッセージが返されます
thread 'main' panicked at 'called `Result::unwrap()` on
an `Err` value: "NOT_AN_EVEN"', libcore\result.rs:945:5
note: Run with `RUST_BACKTRACE=1` for a backtrace
expect()
パニックが発生した場合、プログラムはカスタムエラーメッセージを返すことができます。これを次の例に示します-
use std::fs::File;
fn main(){
let f = File::open("pqr.txt").expect("File not able to open");
//file does not exist
println!("end of main");
}
関数expect()はunwrap()に似ています。唯一の違いは、expectを使用してカスタムエラーメッセージを表示できることです。
出力
thread 'main' panicked at 'File not able to open: Error { repr: Os
{ code: 2, message: "No such file or directory" } }', src/libcore/result.rs:860
note: Run with `RUST_BACKTRACE=1` for a backtrace.
ジェネリックスは、異なるタイプの複数のコンテキストのコードを記述するための機能です。Rustでは、ジェネリックスはデータ型と特性のパラメーター化を指します。Genericsを使用すると、コードの重複を減らし、型安全性を提供することで、より簡潔でクリーンなコードを記述できます。ジェネリックスの概念は、メソッド、関数、構造、列挙、コレクション、および特性に適用できます。
ザ・ <T> syntaxtypeパラメーターとして知られ、ジェネリック構造を宣言するために使用されます。Tは任意のデータ型を表します。
イラスト:ジェネリックコレクション
次の例では、整数のみを格納できるベクトルを宣言しています。
fn main(){
let mut vector_integer: Vec<i32> = vec![20,30];
vector_integer.push(40);
println!("{:?}",vector_integer);
}
出力
[20, 30, 40]
次のスニペットを検討してください-
fn main() {
let mut vector_integer: Vec<i32> = vec![20,30];
vector_integer.push(40);
vector_integer.push("hello");
//error[E0308]: mismatched types
println!("{:?}",vector_integer);
}
上記の例は、整数型のベクトルが格納できるのは整数値のみであることを示しています。したがって、文字列値をコレクションにプッシュしようとすると、コンパイラはエラーを返します。ジェネリックスは、コレクションをよりタイプセーフにします。
イラスト:一般的な構造
typeパラメーターは、コンパイラーが後で入力する型を表します。
struct Data<T> {
value:T,
}
fn main() {
//generic type of i32
let t:Data<i32> = Data{value:350};
println!("value is :{} ",t.value);
//generic type of String
let t2:Data<String> = Data{value:"Tom".to_string()};
println!("value is :{} ",t2.value);
}
上記の例では、Dataという名前の汎用構造を宣言しています。<T>タイプは、いくつかのデータ型を示します。main()の整数インスタンスと文字列インスタンス、構造-機能は、2つのインスタンスを作成します。
出力
value is :350
value is :Tom
特性
特性を使用して、複数の構造にわたって動作(メソッド)の標準セットを実装できます。特性は次のようなものですinterfacesオブジェクト指向プログラミングで。特性の構文は次のとおりです-
特性を宣言する
trait some_trait {
//abstract or method which is empty
fn method1(&self);
// this is already implemented , this is free
fn method2(&self){
//some contents of method2
}
}
トレイトには、具体的なメソッド(本体のあるメソッド)または抽象的なメソッド(本体のないメソッド)を含めることができます。メソッド定義がトレイトを実装するすべての構造で共有される場合は、具体的なメソッドを使用します。ただし、構造体は、トレイトによって定義された関数をオーバーライドすることを選択できます。
メソッド定義が実装構造によって異なる場合は、抽象メソッドを使用してください。
構文-トレイトを実装する
impl some_trait for structure_name {
// implement method1() there..
fn method1(&self ){
}
}
次の例では、構造体bookによって実装されるメソッドprint()を使用してトレイトPrintableを定義します。
fn main(){
//create an instance of the structure
let b1 = Book {
id:1001,
name:"Rust in Action"
};
b1.print();
}
//declare a structure
struct Book {
name:&'static str,
id:u32
}
//declare a trait
trait Printable {
fn print(&self);
}
//implement the trait
impl Printable for Book {
fn print(&self){
println!("Printing book with id:{} and name {}",self.id,self.name)
}
}
出力
Printing book with id:1001 and name Rust in Action
ジェネリック関数
この例では、渡されたパラメーターを表示するジェネリック関数を定義しています。パラメータはどのタイプでもかまいません。パラメータのタイプは、その値をprintlnで出力できるように、Displayトレイトを実装する必要があります。大きい。
use std::fmt::Display;
fn main(){
print_pro(10 as u8);
print_pro(20 as u16);
print_pro("Hello TutorialsPoint");
}
fn print_pro<T:Display>(t:T){
println!("Inside print_pro generic function:");
println!("{}",t);
}
出力
Inside print_pro generic function:
10
Inside print_pro generic function:
20
Inside print_pro generic function:
Hello TutorialsPoint
この章では、標準入力(キーボード)から値を受け入れ、標準出力(コンソール)に値を表示する方法について説明します。この章では、コマンドライン引数の受け渡しについても説明します。
リーダーとライターのタイプ
入力と出力のためのRustの標準ライブラリ機能は、2つの特性を中心に構成されています-
- Read
- Write
シニア番号 | 特性と説明 | 例 |
---|---|---|
1 | Read Readを実装する型には、バイト指向の入力用のメソッドがあります。彼らは読者と呼ばれています |
Stdin、File |
2 | Write 書き込みを実装するタイプは、バイト指向とUTF-8の両方のテキスト出力をサポートします。彼らは作家と呼ばれています。 |
Stdout、File |
特性を読む
Readersプログラムがバイトを読み取ることができるコンポーネントです。例としては、キーボードやファイルなどからの入力の読み取りがあります。read_line() この特性の方法を使用して、ファイルまたは標準入力ストリームから一度に1行ずつデータを読み取ることができます。
シニア番号 | 特性 | 方法と説明 |
---|---|---|
1 | 読んだ | read_line(&mut line)->Result テキストの行を読み取り、それを文字列である行に追加します。戻り値は、読み取られたバイト数であるio :: Resultです。 |
イラスト-コンソールからの読み取り-stdin()
Rustプログラムは、実行時にユーザーからの値を受け入れる必要がある場合があります。次の例では、標準入力(キーボード)から値を読み取り、コンソールに出力します。
fn main(){
let mut line = String::new();
println!("Enter your name :");
let b1 = std::io::stdin().read_line(&mut line).unwrap();
println!("Hello , {}", line);
println!("no of bytes read , {}", b1);
}
STDIN()関数は、そのために、現在のプロセスの標準入力ストリームへのハンドルを返すREAD_LINE関数を適用することができます。この関数は、行末文字を検出すると、入力バッファーに存在するすべての文字を読み取ろうとします。
出力
Enter your name :
Mohtashim
Hello , Mohtashim
no of bytes read , 10
トレイトを書く
Writersプログラムがバイトを書き込むことができるコンポーネントです。例としては、コンソールへの値の出力、ファイルへの書き込みなどがあります。この特性のwrite()メソッドを使用して、ファイルまたは標準出力ストリームにデータを書き込むことができます。
シニア番号 | 特性 | 方法と説明 |
---|---|---|
1 | 書く | write(&buf)->Result スライスbufのバイトの一部を基になるストリームに書き込みます。書き込まれたバイト数であるio :: Resultを返します。 |
イラスト-コンソールへの書き込み-stdout()
プリント!またはprintln!マクロを使用して、コンソールにテキストを表示できます。ただし、write()標準ライブラリ関数を使用して、標準出力にテキストを表示することもできます。
これを理解するための例を考えてみましょう。
use std::io::Write;
fn main() {
let b1 = std::io::stdout().write("Tutorials ".as_bytes()).unwrap();
let b2 = std::io::stdout().write(String::from("Point").as_bytes()).unwrap();
std::io::stdout().write(format!("\nbytes written {}",(b1+b2)).as_bytes()).unwrap();
}
出力
Tutorials Point
bytes written 15
STDOUT()標準ライブラリ関数は、現在のプロセスの標準出力ストリームへのハンドルを返すまでwrite機能を適用することができます。write()メソッドは列挙型Resultを返します。unwrap()は、列挙から実際の結果を抽出するためのヘルパーメソッドです。エラーが発生した場合、unwrapメソッドはパニックを送信します。
NOTE −ファイルIOについては、次の章で説明します。
コマンドライン引数
コマンドライン引数は、プログラムを実行する前にプログラムに渡されます。これらは、関数に渡されるパラメーターのようなものです。CommandLineパラメーターを使用して、main()関数に値を渡すことができます。ザ・std::env::args() コマンドライン引数を返します。
図
次の例では、値をcommandLine引数としてmain()関数に渡します。プログラムは、ファイル名main.rsで作成されます。
//main.rs
fn main(){
let cmd_line = std::env::args();
println!("No of elements in arguments is :{}",cmd_line.len());
//print total number of values passed
for arg in cmd_line {
println!("[{}]",arg); //print all values passed
as commandline arguments
}
}
プログラムは、コンパイルされるとファイルmain.exeを生成します。複数のコマンドラインパラメータはスペースで区切る必要があります。などの端末からMAIN.EXEを実行MAIN.EXEハローtutorialspoint。
NOTE-ハローとtutorialspointは、コマンドライン引数です。
出力
No of elements in arguments is :3
[main.exe]
[hello]
[tutorialspoint]
main.exeが最初の引数であるため、出力には3つの引数が表示されます。
図
次のプログラムは、コマンドライン引数として渡された値の合計を計算します。スペースで区切られた整数値のリストがプログラムに渡されます。
fn main(){
let cmd_line = std::env::args();
println!("No of elements in arguments is
:{}",cmd_line.len());
// total number of elements passed
let mut sum = 0;
let mut has_read_first_arg = false;
//iterate through all the arguments and calculate their sum
for arg in cmd_line {
if has_read_first_arg { //skip the first argument since it is the exe file name
sum += arg.parse::<i32>().unwrap();
}
has_read_first_arg = true;
// set the flag to true to calculate sum for the subsequent arguments.
}
println!("sum is {}",sum);
}
プログラムをmain.exe1 2 3 4として実行すると、出力は次のようになります。
No of elements in arguments is :5
sum is 10
Rustでは、コンソールの読み取りと書き込みに加えて、ファイルの読み取りと書き込みが可能です。
File構造体はファイルを表します。これにより、プログラムはファイルに対して読み取り/書き込み操作を実行できます。File構造体のすべてのメソッドは、io :: Result列挙のバリアントを返します。
File構造体の一般的に使用されるメソッドを以下の表に示します-
シニア番号 | モジュール | 方法 | 署名 | 説明 |
---|---|---|---|---|
1 | std :: fs :: File | 開いた() | pub fn open <P:AsRef>(パス:P)->結果 | open staticメソッドを使用して、ファイルを読み取り専用モードで開くことができます。 |
2 | std :: fs :: File | create() | pub fn create <P:AsRef>(パス:P)->結果 | 静的メソッドは、書き込み専用モードでファイルを開きます。ファイルがすでに存在する場合、古いコンテンツは破棄されます。それ以外の場合は、新しいファイルが作成されます。 |
3 | std :: fs :: remove_file | remove_file() | pub fn remove_file <P:AsRef>(パス:P)->結果<()> | ファイルシステムからファイルを削除します。ファイルがすぐに削除される保証はありません。 |
4 | std :: fs :: OpenOptions | append() | pub fn append(&mut self、append:bool)->&mut OpenOptions | ファイルの追加モードのオプションを設定します。 |
5 | std :: io :: Writes | write_all() | fn write_all(&mut self、buf:&[u8])->結果<()> | バッファ全体をこの書き込みに書き込もうとします。 |
6 | std :: io :: Read | read_to_string() | fn read_to_string(&mut self、buf:&mut String)->結果 | このソースのEOFまでのすべてのバイトを読み取り、bufに追加します。 |
ファイルへの書き込み
ファイルの書き方を理解するための例を見てみましょう。
次のプログラムは、ファイル 'data.txt'を作成します。create()メソッドは、ファイルを作成するために使用されます。ファイルが正常に作成された場合、メソッドはファイルハンドルを返します。最後の行のwrite_all関数は、新しく作成されたファイルにバイトを書き込みます。いずれかの操作が失敗した場合、expect()関数はエラーメッセージを返します。
use std::io::Write;
fn main() {
let mut file = std::fs::File::create("data.txt").expect("create failed");
file.write_all("Hello World".as_bytes()).expect("write failed");
file.write_all("\nTutorialsPoint".as_bytes()).expect("write failed");
println!("data written to file" );
}
出力
data written to file
ファイルから読み取る
次のプログラムは、ファイルdata.txtの内容を読み取り、コンソールに出力します。「開く」機能は、既存のファイルを開くために使用されます。ファイルへの絶対パスまたは相対パスは、パラメーターとしてopen()関数に渡されます。open()関数は、ファイルが存在しない場合、または何らかの理由でファイルにアクセスできない場合に例外をスローします。成功すると、そのようなファイルへのファイルハンドルが「file」変数に割り当てられます。
「file」ハンドルの「read_to_string」関数は、そのファイルの内容を文字列変数に読み込むために使用されます。
use std::io::Read;
fn main(){
let mut file = std::fs::File::open("data.txt").unwrap();
let mut contents = String::new();
file.read_to_string(&mut contents).unwrap();
print!("{}", contents);
}
出力
Hello World
TutorialsPoint
ファイルを削除する
次の例では、remove_file()関数を使用してファイルを削除します。エラーが発生した場合、expect()関数はカスタムメッセージを返します。
use std::fs;
fn main() {
fs::remove_file("data.txt").expect("could not remove file");
println!("file is removed");
}
出力
file is removed
ファイルにデータを追加する
append()関数は、ファイルの最後にデータを書き込みます。これを以下の例に示します-
use std::fs::OpenOptions;
use std::io::Write;
fn main() {
let mut file = OpenOptions::new().append(true).open("data.txt").expect(
"cannot open file");
file.write_all("Hello World".as_bytes()).expect("write failed");
file.write_all("\nTutorialsPoint".as_bytes()).expect("write failed");
println!("file append success");
}
出力
file append success
ファイルをコピーする
次の例では、ファイルの内容を新しいファイルにコピーします。
use std::io::Read;
use std::io::Write;
fn main() {
let mut command_line: std::env::Args = std::env::args();
command_line.next().unwrap();
// skip the executable file name
// accept the source file
let source = command_line.next().unwrap();
// accept the destination file
let destination = command_line.next().unwrap();
let mut file_in = std::fs::File::open(source).unwrap();
let mut file_out = std::fs::File::create(destination).unwrap();
let mut buffer = [0u8; 4096];
loop {
let nbytes = file_in.read(&mut buffer).unwrap();
file_out.write(&buffer[..nbytes]).unwrap();
if nbytes < buffer.len() { break; }
}
}
上記のプログラムをmain.exedata.txtdatacopy.txtとして実行します。ファイルの実行中に2つのコマンドライン引数が渡されます-
- ソースファイルへのパス
- 宛先ファイル
CargoはRUSTのパッケージマネージャーです。これはツールのように機能し、Rustプロジェクトを管理します。
一般的に使用される貨物コマンドのいくつかを以下の表に示します-
シニア番号 | コマンドと説明 |
---|---|
1 | cargo build 現在のプロジェクトをコンパイルします。 |
2 | cargo check 現在のプロジェクトを分析してエラーを報告しますが、オブジェクトファイルは作成しません。 |
3 | cargo run src /main.rsをビルドして実行します。 |
4 | cargo clean ターゲットディレクトリを削除します。 |
5 | cargo update Cargo.lockにリストされている依存関係を更新します。 |
6 | cargo new 新しい貨物プロジェクトを作成します。 |
Cargoは、サードパーティのライブラリをダウンロードするのに役立ちます。したがって、パッケージマネージャーのように機能します。独自のライブラリを構築することもできます。Rustをインストールすると、デフォルトでCargoがインストールされます。
新しい貨物プロジェクトを作成するには、以下のコマンドを使用できます。
バイナリクレートを作成する
cargo new project_name --bin
ライブラリクレートを作成する
cargo new project_name --lib
貨物の現在のバージョンを確認するには、次のコマンドを実行します-
cargo --version
イラスト-バイナリカーゴプロジェクトを作成する
ゲームは乱数を生成し、ユーザーにその数を推測するように促します。
ステップ1-プロジェクトフォルダを作成する
ターミナルを開き、次のコマンドを入力します。cargonewguess-game-app--bin。
これにより、次のフォルダ構造が作成されます。
guess-game-app/
-->Cargo.toml
-->src/
main.rs
貨物新しいコマンドはクレートを作成するために使用されます。--binフラグが作成されるクレートバイナリクレートであることを示しています。パブリッククレートは、crates.ioと呼ばれる中央リポジトリに保存されますhttps://crates.io/。
ステップ2-外部ライブラリへの参照を含める
この例では、乱数を生成する必要があります。内部標準ライブラリは乱数生成ロジックを提供しないため、外部ライブラリまたはクレートを調べる必要があります。使用しましょうrandcrates.ioウェブサイトcrates.ioで入手可能なクレート
ザ・ https://crates.io/crates/randは乱数生成用のラストライブラリです。Randは、乱数を生成し、それらを有用な型と分布に変換するためのユーティリティ、およびいくつかのランダム性関連のアルゴリズムを提供します。
次の図は、crate.ioWebサイトとrandcrateの検索結果を示しています。
randcrateのバージョンをCargo.tomlファイルrand = " 0.5.5 "にコピーします。
[package]
name = "guess-game-app"
version = "0.1.0"
authors = ["Mohtashim"]
[dependencies]
rand = "0.5.5"
ステップ3:プロジェクトをコンパイルする
プロジェクトフォルダに移動します。コマンドを実行しますcargo build ターミナルウィンドウ上-
Updating registry `https://github.com/rust-lang/crates.io-index`
Downloading rand v0.5.5
Downloading rand_core v0.2.2
Downloading winapi v0.3.6
Downloading rand_core v0.3.0
Compiling winapi v0.3.6
Compiling rand_core v0.3.0
Compiling rand_core v0.2.2
Compiling rand v0.5.5
Compiling guess-game-app v0.1.0
(file:///E:/RustWorks/RustRepo/Code_Snippets/cargo-projects/guess-game-app)
Finished dev [unoptimized + debuginfo] target(s) in 1m 07s
ランドクレートとすべての推移的な依存関係(ランドの内部依存関係)が自動的にダウンロードされます。
ステップ4-ビジネスロジックを理解する
数字推測ゲームでビジネスロジックがどのように機能するかを見てみましょう-
ゲームは最初に乱数を生成します。
ユーザーは、入力を入力して番号を推測するように求められます。
数値が生成された数値よりも小さい場合、「低すぎます」というメッセージが出力されます。
数値が生成された数値よりも大きい場合、「高すぎます」というメッセージが出力されます。
ユーザーがプログラムによって生成された番号を入力すると、ゲームは終了します。
ステップ5-main.rsファイルを編集する
main.rsファイルにビジネスロジックを追加します。
use std::io;
extern crate rand;
//importing external crate
use rand::random;
fn get_guess() -> u8 {
loop {
println!("Input guess") ;
let mut guess = String::new();
io::stdin().read_line(&mut guess)
.expect("could not read from stdin");
match guess.trim().parse::<u8>(){ //remember to trim input to avoid enter spaces
Ok(v) => return v,
Err(e) => println!("could not understand input {}",e)
}
}
}
fn handle_guess(guess:u8,correct:u8)-> bool {
if guess < correct {
println!("Too low");
false
} else if guess> correct {
println!("Too high");
false
} else {
println!("You go it ..");
true
}
}
fn main() {
println!("Welcome to no guessing game");
let correct:u8 = random();
println!("correct value is {}",correct);
loop {
let guess = get_guess();
if handle_guess(guess,correct){
break;
}
}
}
ステップ6-プロジェクトをコンパイルして実行する
ターミナルでコマンドcargorunを実行します。ターミナルがプロジェクトディレクトリを指していることを確認してください。
Welcome to no guessing game
correct value is 97
Input guess
20
Too low
Input guess
100
Too high
Input guess
97
You got it ..
この章では、イテレータとクロージャがRUSTでどのように機能するかを学習します。
イテレータ
イテレータは、配列、ベクトル、マップなどの値のコレクションを反復処理するのに役立ちます。イテレータは、Rust標準ライブラリで定義されているイテレータ特性を実装します。ITER()メソッドは、コレクションのイテレータオブジェクトを返します。イテレータオブジェクトの値はアイテムと呼ばれます。イテレータのnext()メソッドを使用して、アイテムをトラバースできます。次の()メソッドは、それがコレクションの最後に到達したときに値Noneを返します。
次の例では、イテレータを使用して配列から値を読み取ります。
fn main() {
//declare an array
let a = [10,20,30];
let mut iter = a.iter();
// fetch an iterator object for the array
println!("{:?}",iter);
//fetch individual values from the iterator object
println!("{:?}",iter.next());
println!("{:?}",iter.next());
println!("{:?}",iter.next());
println!("{:?}",iter.next());
}
出力
Iter([10, 20, 30])
Some(10)
Some(20)
Some(30)
None
配列やVectorなどのコレクションがイテレータ特性を実装している場合は、以下に示すようにfor ... in構文を使用してトラバースできます-
fn main() {
let a = [10,20,30];
let iter = a.iter();
for data in iter{
print!("{}\t",data);
}
}
出力
10 20 30
次の3つのメソッドは、コレクションからイテレータオブジェクトを返します。ここで、Tはコレクション内の要素を表します。
シニア番号 | 方法と説明 |
---|---|
1 | iter() &T(Tへの参照)にイテレータを与えます |
2 | into_iter() T上でイテレータを与える |
3 | iter_mut() &mutTにイテレータを与える |
イラスト:iter()
iter()関数は借用の概念を使用します。コレクションの各要素への参照を返し、コレクションはそのままにして、ループ後に再利用できるようにします。
fn main() {
let names = vec!["Kannan", "Mohtashim", "Kiran"];
for name in names.iter() {
match name {
&"Mohtashim" => println!("There is a rustacean among us!"),
_ => println!("Hello {}", name),
}
}
println!("{:?}",names);
// reusing the collection after iteration
}
出力
Hello Kannan
There is a rustacean among us!
Hello Kiran
["Kannan", "Mohtashim", "Kiran"]
イラスト-into_iter()
この関数は、所有権の概念を使用します。コレクション内の値をiterオブジェクトに移動します。つまり、コレクションは消費され、再利用できなくなります。
fn main(){
let names = vec!["Kannan", "Mohtashim", "Kiran"];
for name in names.into_iter() {
match name {
"Mohtashim" => println!("There is a rustacean among us!"),
_ => println!("Hello {}", name),
}
}
// cannot reuse the collection after iteration
//println!("{:?}",names);
//Error:Cannot access after ownership move
}
出力
Hello Kannan
There is a rustacean among us!
Hello Kiran
イラスト-forおよびiter_mut()
この関数はiter()関数に似ています。ただし、この関数はコレクション内の要素を変更できます。
fn main() {
let mut names = vec!["Kannan", "Mohtashim", "Kiran"];
for name in names.iter_mut() {
match name {
&mut "Mohtashim" => println!("There is a rustacean among us!"),
_ => println!("Hello {}", name),
}
}
println!("{:?}",names);
//// reusing the collection after iteration
}
出力
Hello Kannan
There is a rustacean among us!
Hello Kiran
["Kannan", "Mohtashim", "Kiran"]
閉鎖
クロージャとは、別の関数内の関数を指します。これらは無名関数です–名前のない関数です。クロージャーは、変数に関数を割り当てるために使用できます。これにより、プログラムは関数をパラメータとして他の関数に渡すことができます。クロージャは、インライン関数とも呼ばれます。外部関数の変数には、インライン関数からアクセスできます。
構文:クロージャの定義
クロージャ定義には、オプションでパラメータを含めることができます。パラメータは2本の縦棒で囲まれています。
let closure_function = |parameter| {
//logic
}
クロージャを呼び出す構文は実装します Fn特性。だから、それはで呼び出すことができます() 構文。
closure_function(parameter); //invoking
図
次の例では、関数main()内でクロージャis_evenを定義しています。クロージャは、数値が偶数の場合はtrueを返し、数値が奇数の場合はfalseを返します。
fn main(){
let is_even = |x| {
x%2==0
};
let no = 13;
println!("{} is even ? {}",no,is_even(no));
}
出力
13 is even ? false
図
fn main(){
let val = 10;
// declared outside
let closure2 = |x| {
x + val //inner function accessing outer fn variable
};
println!("{}",closure2(2));
}
メイン()関数は、変数を宣言ヴァルや閉鎖を。クロージャは、外部関数main()で宣言された変数にアクセスします。
出力
12
Rustは、デフォルトでスタック上のすべてを割り当てます。Boxのようなスマートポインタでそれらをラップすることにより、ヒープに物を格納することができます。VecやStringのようなタイプは、暗黙的にヒープの割り当てに役立ちます。スマートポインタは、以下の表にリストされている特性を実装します。スマートポインタのこれらの特性は、通常の構造体とは異なります。
シニア番号 | 特性名 | パッケージと説明 |
---|---|---|
1 | Deref | std::ops::Deref * vなどの不変の逆参照操作に使用されます。 |
2 | 落とす | std::ops::Drop 値がスコープ外になったときにコードを実行するために使用されます。これはデストラクタと呼ばれることもあります |
この章では、 Boxスマートポインタ。Boxのようなカスタムスマートポインターを作成する方法も学びます。
ボックス
ボックスとも呼ばれるボックススマートポインタを使用すると、データをスタックではなくヒープに格納できます。スタックには、ヒープデータへのポインタが含まれています。Boxには、データをヒープに格納する以外に、パフォーマンスのオーバーヘッドはありません。
ボックスを使用してi32値をヒープに格納する方法を見てみましょう。
fn main() {
let var_i32 = 5;
//stack
let b = Box::new(var_i32);
//heap
println!("b = {}", b);
}
出力
b = 5
変数が指す値にアクセスするには、逆参照を使用します。*は間接参照演算子として使用されます。Boxで間接参照を使用する方法を見てみましょう。
fn main() {
let x = 5;
//value type variable
let y = Box::new(x);
//y points to a new value 5 in the heap
println!("{}",5==x);
println!("{}",5==*y);
//dereferencing y
}
変数xは、値5の値型です。したがって、式5 == xはtrueを返します。変数yはヒープを指します。ヒープ内の値にアクセスするには、* yを使用して逆参照する必要があります。* yは値5を返します。したがって、式5 == * yはtrueを返します。
出力
true
true
イラスト-Derefトレイト
標準ライブラリによって提供されるDeref特性では、derefという名前の1つのメソッドを実装する必要があります。このメソッドは、selfを借用し、内部データへの参照を返します。次の例では、ジェネリック型である構造体MyBoxを作成します。トレイトDerefを実装します。この特性は、* yを使用してyでラップされたヒープ値にアクセスするのに役立ちます。
use std::ops::Deref;
struct MyBox<T>(T);
impl<T> MyBox<T> {
// Generic structure with static method new
fn new(x:T)-> MyBox<T> {
MyBox(x)
}
}
impl<T> Deref for MyBox<T> {
type Target = T;
fn deref(&self) -> &T {
&self.0 //returns data
}
}
fn main() {
let x = 5;
let y = MyBox::new(x);
// calling static method
println!("5==x is {}",5==x);
println!("5==*y is {}",5==*y);
// dereferencing y
println!("x==*y is {}",x==*y);
//dereferencing y
}
出力
5==x is true
5==*y is true
x==*y is true
イラスト-ドロップトレイト
Dropトレイトにはdrop()メソッドが含まれています。このメソッドは、この特性を実装した構造がスコープ外になったときに呼び出されます。一部の言語では、プログラマーは、スマートポインターのインスタンスの使用を終了するたびに、コードを呼び出してメモリまたはリソースを解放する必要があります。Rustでは、Dropトレイトを使用して自動メモリ割り当て解除を実現できます。
use std::ops::Deref;
struct MyBox<T>(T);
impl<T> MyBox<T> {
fn new(x:T)->MyBox<T>{
MyBox(x)
}
}
impl<T> Deref for MyBox<T> {
type Target = T;
fn deref(&self) -< &T {
&self.0
}
}
impl<T> Drop for MyBox<T>{
fn drop(&mut self){
println!("dropping MyBox object from memory ");
}
}
fn main() {
let x = 50;
MyBox::new(x);
MyBox::new("Hello");
}
上記の例では、ヒープ内に2つのオブジェクトを作成しているため、dropメソッドが2回呼び出されます。
dropping MyBox object from memory
dropping MyBox object from memory
並行プログラミングでは、プログラムのさまざまな部分が独立して実行されます。一方、並列プログラミングでは、プログラムのさまざまな部分が同時に実行されます。より多くのコンピューターが複数のプロセッサーを利用するため、両方のモデルは等しく重要です。
スレッド
スレッドを使用してコードを同時に実行できます。現在のオペレーティングシステムでは、実行されたプログラムのコードが1つのプロセスで実行され、オペレーティングシステムが複数のプロセスを一度に管理します。プログラム内で、同時に実行される独立した部分を持つこともできます。これらの独立した部分を実行する機能は、スレッドと呼ばれます。
スレッドの作成
ザ・ thread::spawn関数は、新しいスレッドを作成するために使用されます。スポーン関数は、パラメーターとしてクロージャを取ります。クロージャーは、スレッドによって実行される必要があるコードを定義します。次の例では、メインスレッドからのテキストと、新しいスレッドからのその他のテキストを印刷します。
//import the necessary modules
use std::thread;
use std::time::Duration;
fn main() {
//create a new thread
thread::spawn(|| {
for i in 1..10 {
println!("hi number {} from the spawned thread!", i);
thread::sleep(Duration::from_millis(1));
}
});
//code executed by the main thread
for i in 1..5 {
println!("hi number {} from the main thread!", i);
thread::sleep(Duration::from_millis(1));
}
}
出力
hi number 1 from the main thread!
hi number 1 from the spawned thread!
hi number 2 from the main thread!
hi number 2 from the spawned thread!
hi number 3 from the main thread!
hi number 3 from the spawned thread!
hi number 4 from the spawned thread!
hi number 4 from the main thread!
メインスレッドは1から4までの値を出力します。
NOTE−メインスレッドが終了すると、新しいスレッドは停止します。このプログラムからの出力は、毎回少し異なる場合があります。
ザ・ thread::sleep関数は、スレッドにその実行を短時間停止させ、別のスレッドを実行できるようにします。スレッドはおそらく交代で実行されますが、それは保証されていません。オペレーティングシステムがスレッドをスケジュールする方法によって異なります。この実行では、生成されたスレッドからのprintステートメントがコードの最初に表示されますが、メインスレッドが最初に出力されます。さらに、生成されたスレッドが9までの値を出力するようにプログラムされている場合でも、メインスレッドがシャットダウンする前に5にしか到達しませんでした。
ハンドルを結合する
生成されたスレッドは、実行または完全に実行される機会がない場合があります。これは、メインスレッドがすぐに完了するためです。関数spawn <F、T>(f:F)-> JoinHandlelt; T>はJoinHandleを返します。JoinHandleのjoin()メソッドは、関連付けられたスレッドが終了するのを待ちます。
use std::thread;
use std::time::Duration;
fn main() {
let handle = thread::spawn(|| {
for i in 1..10 {
println!("hi number {} from the spawned thread!", i);
thread::sleep(Duration::from_millis(1));
}
});
for i in 1..5 {
println!("hi number {} from the main thread!", i);
thread::sleep(Duration::from_millis(1));
}
handle.join().unwrap();
}
出力
hi number 1 from the main thread!
hi number 1 from the spawned thread!
hi number 2 from the spawned thread!
hi number 2 from the main thread!
hi number 3 from the spawned thread!
hi number 3 from the main thread!
hi number 4 from the main thread!
hi number 4 from the spawned thread!
hi number 5 from the spawned thread!
hi number 6 from the spawned thread!
hi number 7 from the spawned thread!
hi number 8 from the spawned thread!
hi number 9 from the spawned thread!
メインスレッドとスポーンされたスレッドは切り替えを続けます。
NOTE −メインスレッドは、への呼び出しのために、生成されたスレッドが完了するのを待ちます join() 方法。