Chapter 04

データ型

📌 基本データ型

Rust の標準にある基本的なデータ型は次のとおりです:

  • 整数型: i8, u8, i16, u16, i32, u32, i64, u64, isize, usize
  • 浮動小数点型: f32, f64
  • ブーリアン型: bool
  • 文字列型: char
  • タプル(複合)型: (500, 6.4, true), () はユニット.
  • 配列型: [1,2,3,4,5], [3;5] = [3,3,3,3,3]

数値型のリテラルには次のものが使えます:

  • 98_222 (10進数)
  • 0xff (16進数)
  • 0o77 (8進数)
  • 0b1111_0000 (2進数)
  • b'A' (バイト)
  • 0. (浮動小数点数)

基本的なデータ型はコピートレイトを実装しています.また,複合・配列型については,含まれている要素がすべてコピートレイトを実装していれば,全体もコピートレイトを実装したことになります.
参照も型の1つで,不変参照はコピートレイトを実装していますし,可変参照は実装していません.また,参照のまた参照ということも可能です.

fn main() {
    let a = 42;
    let ref_ref_ref_a = &&&a;
    let ref_a = **ref_ref_ref_a;
    let b = *ref_a;
    print!("{} {}", a, b);
}

比較するときは基本的に同じ型でなくてはならないので,参照もまた型であるということが以下でわかります.

fn main() {
    let a         = 10;     // immutable object
    let a_ref     = &a;     // reference
    let a_ref_ref = &a_ref; // reference to reference
    println!("{}", a == a_ref);
    // error[E0277]: can't compare `{integer}` with `&{integer}`
    println!("{}", a_ref_ref == a_ref);
    // error[E0277]: can't compare `&{integer}` with `{integer}`
}

Rust は強い型推論を持っていますが,意図的にデータ型を指定したい場合があります.その場合は変数名の後ろに : を付けてデータ型を指定します.

fn main() {
    let a: i32 = 10;
    let b: u32 = 20;
    let c: f32 = 0.;
    let d: &i32 = &50;
    print!("{} {} {} {}", a, b, c, d);
}

変数は パターン を使って,要素を分解して束縛することが出来ます.

fn main() {
    let (x,y,z) = (1,2,3);
    let [a,b,c] = [4,5,6];
    let (i,_,_) = (7,8,9);
    println!("xyz= {} {} {}", x, y, z);
    println!("abc= {} {} {}", a, b, c);
    println!("  i= {}", i);
}

_ワイルドカード と呼ばれるもので,オブジェクトを無視するときに使います.
Rust では同じスコープ内で,変数名を使い回すことができます.

fn main() {
    let str_len = String::from("hello world!");
    let str_len = str_len.len();
    println!("{}", str_len);
}

あるスコープのさらにローカルなスコープにおいても外側にある変数名と同じ名前で新しく束縛できます.このとき,外側の変数はローカルから隠れます.これを シャドーイング と言います.

fn main() {
    let a = 10;
    
    { // local scope
        let mut a = 20;
        a += 30;
        println!("{}", a); // 50
    }
    
    println!("{}", a); // 10
}

明示的に数値オブジェクトを型変換して使いたい場合があります.その場合は as を使います.

fn main() {
    let a = 13u8;
    let b = 7u32;
    let c = a as u32 + b;
    println!("{}", c);

    let t = true;
    println!("{}", t as u8);
}

📌 スライス

スライス は参照の1つで,別のオブジェクト内の連続した要素を指し示すものです.スライスを取得するには,オブジェクトに対して 数列指定[m..n])します.参照なので,スライスの型は例えば配列だと &[T] となります. T は任意の型です.

fn main() {
    let a = [1,2,3,4,5];
    let a_slice = &a[1..3];
    dbg!(a_slice); // [2,3]
}

数列

数列は Range 型と呼ばれるもので m..n の形で指定します. mn はそれぞれ開始値と終了値で, m から n-1 までの連番を表します. m..=n とすることで, m から n までの連番を表します.数列は for 式で利用することができます.

fn main() {
    let mut sum=0;
    for i in 1..100 {
        sum += i;
    }
    println!("sum={}", sum);
}

Rust はインデックスが 0 から始まります.数列指定では開始インデックスと終了インデックスを .. を使って指定します.例えば m..n なら [m, m+1, m+2, …, n-1] となります. m..=n の場合は [m, m+1, m+2, …, n] となります.開始インデックスと終了インデックスは省略することが出来ます.

let s = String::from("hello");
let slice = &s[0..2];
let slice = &s[0..=2];
let slice = &s[..2];
let slice = &s[3..s.len()];
let slice = &s[3..];
let slice = &s[..];

文字列リテラル

文字列のスライスの型は &str です.そして,文字列リテラルは不変の文字列スライス (&str) です.

let s = "Hello, world!";