広告 Rust プログラミング 入門

新米プログラマーによるRust入門-その3(型)

2020年8月27日

Rust

今回はプログラミング言語「Rust」のについて説明していこうと思います。
正直、普段使っていたJavaやC#、C++等とは宣言方法が違ったので自分のためにも書いておきたいなーということで書かせていただきます。
前回のRustに関する記事はこちら

[st-card-ex url="https://www.dice-programming-etc.com/%e6%96%b0%e7%b1%b3%e3%83%97%e3%83%ad%e3%82%b0%e3%83%a9%e3%83%9e%e3%83%bc%e3%81%ab%e3%82%88%e3%82%8brust%e5%85%a5%e9%96%80-%e3%81%9d%e3%81%ae%ef%bc%92%ef%bc%88%e5%9b%9b%e5%89%87%e6%bc%94%e7%ae%97/" target="_blank" rel="nofollow" label="" name="" bgcolor="" color="" readmore=""]

早速やっていきましょう。

機械語型

機械語型......聞きなれないですねぇ……。
これは簡単に言うと、整数型浮動小数点型文字型等の方のことですね。
他の言語に触れたことのある人は「あ、整数型ってint型とかだ」と思ってもらえると思います。
まぁ、宣言方法が少し違うんですよねぇ......。

整数型

分かる人はお察しの通り、int型とかその辺のやつですね。
JavaとかC言語等には整数型として「byte型、short型、int型、long型」というのがありましたね。
Rustだと少し宣言方法が違うんですよ。まずは型を見てみましょう。

範囲
u80~28 -1(0~255)
u160~216 -1(0~65,535)
u320~232 -1(0~4,294,967,295)
u640~264 -1(0~18,446,744,073,709,551,615)
usize0~232 -1 または 264 -1
符号なし
範囲
i8-27~27-1(-128~127)
i16-215~215-1(-32,768~32,767)
i32-231~231-1(-2,147,483,648~2,147,483,647)
i64-263~263-1(-9,223,372,036,854,775,808~9,223,372,036,854,775,807)
isize-231~231-1 または -263~263-1
符号あり

もうね......、結構違いますね。
で、各表の「usizeisize」ですが、「または」となっていますね。
ここは小さい方は32bitで大きい方が64bitだと思ってくれればOKです。
使っているPCのOSが32bitか64bitかで変わってきますが、最近はだいたい64bitですね。
ちなみにRustでは配列の要素数はusizeにしなければならないそうです。
C言語やC++と違って文字は文字、数値は数値で分けて扱います。

浮動小数点型

はい、これは「double型float型」の事ですね。これもまた整数型と同じように違いがあります。

C++で対応する型(多分)範囲
f32float約-3.4×1038~3.4×1038
f64double約-1.8×10308~1.8×10308
浮動小数点型

こんな感じですね。整数型ほど説明する事は無いですね。次!

真偽値型

C++で言うbool型やJavaで言うboolean型とかですね。何か特別に型があるのか......と言うところですが、boolです。はい。次!

文字

char型の事ですね。はい。
明示的に宣言する必要はないですが、宣言するときはシングルクォーテーション「'」で囲むことでchar型として宣言できます。
特殊な文字を扱う場合はバックスラッシュで文字をエスケープします。

文字文字リテラル
シングルクォーテーション(')'\''
バックスラッシュ(\)'\\'
改行'\n'
リターン'\r'
タブ'\t'
エスケープ

タプル

タプルは要素を複数持った型でになります。宣言としては要素を「,」で区切り、「()」で囲む。

let taple = (123, "abcde", 3.14);

println!("{}",taple.0);
println!("{}",taple.1);
println!("{}",taple.2);

こんな感じに型関係なく複数宣言できます。printlnの中で「taple.~」と書いていると思いますが、タプルはこんな感じで呼び出します。結構便利ですね。

ポインタ型

ポインタってC言語系にもありましたけど、まぁまともに理解しようとすると結構難しいですよねー。はい。ざっと説明していこうかなと思います。(私も完全に理解できていないので勉強の記録も兼ねています。)

参照

参照ですね......。
参照はスタック・ヒープを問わず、どんな値でも指すことができるポインタの事です。
参照は「&」で借用(Rustでは借用と呼ぶ)し「*」でポインタの指す値を取得します。はい。
よくわからないのでここを見て勉強します!

[st-card-ex url="https://doc.rust-jp.rs/book/second-edition/ch04-02-references-and-borrowing.html" target="_blank" rel="nofollow" label="" name="" bgcolor="" color="" readmore=""]

Box

メモリをヒープ上に動的に確保するには「Box::new」を使用します。
こんな感じに使用します。

let a = (111 , "あいうえお");
let b = Box::new(a);

rawポインタ

C・C++のポインタに似ています。ただ、unsafeブロックの中でしか使用できません
単純に、エラーの責任はコンパイラではなくプログラマーが負うことになります。

配列・ベクタ・スライス

配列は聞いたことがあるかもしれませんが「ベクタ?」「スライス?」となりませんか?
私はなりました。はい。そんなことより、宣言の方法や「ベクタ・スライス」について説明していきます。

配列

宣言の方法としては以下の二つになります。

let A: [i32; 5] = [1,32,44,23,12];
let B = ["AAA","BBB","CCC","DDD"];

取り出し方は他の言語と同じようにこんな感じにできます。

println!("{}",A[1]);
println!("{}",B[2]);

宣言方法の話に戻りますが、一つ目の宣言方法は何をしているかと言うと

let A: [型; 配列数]

こういうことです。そんなに難しくないですね。多分。

ベクタ

ベクタ(Vec<T>)は、ヒープ上に確保されるサイズの変更が可能な型Tの配列です。
※型Tは各データ型です。
ベクタを作成する方法はいくつかあるようですが、vec!マクロを使用する方法で配列のリテラルとほぼ同じようにベクタを使用することができます。

fn main() {
    let mut v = vec![1,2,3,4,5];
    for i  in v{
        println!("{:?}",i);
    }
}

これで順番に表示されます。
で、ベクタは配列みたいですが別物なので動的に要素を追加することができます。

fn main() {
    let mut v = vec![1,2,3,4,5];
    v.push(11);
    v.push(23);
    for i  in v{
        println!("{:?}",i);
    }
}
fn main() {
    let mut v = vec![1,2,3,4,5];
    v.push(11);
    v.push(23);
    for i  in v{
        println!("{:?}",i);
    }
}

3行目と4行目でベクタに1123を追加しています。このまま実行すれば1123が追加された状態で順番に表示されると思います。
で、このvec!マクロは、新しい空のベクタを「Vec::new」で作ってから要素を追加していくことと同等です。この「Vec::new」も良く使います。

fn main() {
    let mut v = Vec::new();
    v.push(11);
    v.push(23);
    for i  in v{
        println!("{:?}",i);
    }
}

これで実行すれば1123が表示されます。
イテレータが生成する値からベクタを作る方法もあります。

fn main() {
    let v: Vec< i32> = (0..5).collect();
    for i  in v{
        println!("{:?}",i);
    }
}

これで「」の値が出力されます。
ベクタが必要とする要素数が前もって分かっている場合はVec::newではなくVec::with_capacityを用いて要素数を指定してベクタを作ることができる。
ただ、要素数より多い要素をpushした場合でも新しく要素を追加することはできる。ただ、要素数を指定したのであればおススメはしない。

let mut vec1 = Vec::with_capacity(5);

ベクタの要素

ベクタはバッファへのポインタ、バッファの容量、要素数。
ベクタはバッファの長さが容量を超えたときに、新しく2倍の容量のメモリを確保し直し、それまでのバッファをコピーしてベクタのポインタと容量が更新されて新しい要素を指すようになり、古いバッファは開放されます。

スライス

スライスは長さを指定せず[T]のように書きます。
スライスは最初の要素を指すポインタであるファットポインタと、スライスから含まれる要素数から構成される。

fn main(){
    let a:[i32;4] = [1,6,3,2];
    foo(&a);
    let v1: Vec< i32> = (0..5).collect();
    foo(&v1);
}

fn foo(n: &[i32]){
    for i  in n{
        println!("{}",i);
    }
}

これで2行目で設定した値が順番に出力されて、次に4行目で設定した「0~4」が出力されます。

文字列型

C++でのプログラミング経験がある人であれば、C++には文字列を表す方法が2種類存在することは知っていると思います。文字列リテラルはポインタ型のconst char*ですね。あとはC++の標準のライブラリとしてstd::stringクラスが存在しますね。こんな感じのものがRustにも存在します。
順番に紹介していきますね。

文字列リテラル

これは他のプログラミング言語でのプログラミング経験がある人ならわかりそうですが「”」で囲って定義する文字列ですね。エスケープシーケンスを使用したい場合はバックスラッシュ「\」を用いることでエスケープシーケンスを利用できます。(windowsでは半角¥の場合がある)

let talk = "\"Hay!!\"aaaaaa";

こんな感じですね。

改行


文字列中の改行はそのまま出力されるという特徴もあります。

let talk = "\"Hay\"
    aaaaa";

このまま出力すると......

"Hay"
    aaaaa

こんな感じに出力されます。で、行末をバックスラッシュにすることで改行せずに表示されます。

let talk = "\"Hay\"\
    aaaaa";
"Hay"aaaaa

生文字列(raw string)


文字列を使うときに「エスケープめんどくさい」と思うときもあるでしょう。そういう時に使用するのが生文字列です。生文字列を宣言するときは文字列リテラルの前に「r」を付けます。
生文字列中では、エスケープ文字も無視されるので、文字列中でダブルクォーテーションを使用したい場合は、文字列の前後に任意の数の「#」を記述することで使用できるようになります。

let storage = r"C:\Windows\System";
let quotation = r#"""""""#;

バイト文字列


次は「r」ではなく「b」を冒頭につけることで宣言します。バイト文字列はu8(バイト)値からなるスライスになります。

let byte = b"GET";
for b in byte{
        println!("{}",b);
    }

一応表示して中身を確認できるようにしました。これで「71 69 84」が出力されます。
バイト文字列にはエスケープシーケンスも使えますし、生文字列と組み合わせて使用することもできます。
生バイト文字列は冒頭に「br」を付けることで宣言ができます。
此のバイト文字列ですが、Unicode文字は使用できません。使用できるのはASCII文字とエスケープシーケンスのみになります。

String


文字列に対していろいろな操作を行いたいときは、「to_string関数」を呼び出して&strをStringに変換する必要があります。

let str = "DICEのプログラミング部屋".to_string();

詳しい話は追々していきますね。
このデータ型について一つ一つ説明していくと記事が何倍にもなりそうなので少しずつ順番にやっていきますね。

まとめ


データ型についてはこんなものですかね。詳しい内容は書いていくとめちゃくちゃ長くなってしますのでこれくらいにします。あとは、いろいろとやっていきながら「私も」学んでいこうと思います。
次回は、どうしましょうか......。
条件分岐とか繰り返し処理とかやっていきましょうかね。
次回の記事までお楽しみに~。

-Rust, プログラミング, 入門
-, ,