Rustのリテラルと数値の表記について

 

Rustのリテラルと数値の表記について

Rustは、この言語の基礎的な要素の1つとして、リテラルや数値表記を提供しています。本記事では、Rustにおける基本的なリテラルの使い方や形式、数値表記の種類について詳しく解説します。

リテラルとは何か

リテラルとは、Rustのプログラム内で値を直接表示するものです。例えば、数値や文字列、ブール値などが含まれます。リテラルは、いずれかのデータ型に決められています。

主なリテラルの種類:

  • スカラー型: 数値、ブール値、文字、浮動小数点値など
  • コレクション型: リスト、配列、タプルなど

Rustのリテラルは、使いやすさと実行性を考慮した設計になっています。

数値の表記の種類

Rustでは、複数の数値表記が提供されており、それぞれにデータ型が指定されます。主な数値の表記は以下の通りです。

正数と負数

  • 42 (正数)
  • -42 (è² æ•°)

十六進数表記

  • 0x2A (十六進数の42)

八進数表記

  • 0o52 (八進数の42)

二進数表記

  • 0b101010 (二進数の42)

小数点を含む値

型指定定数

Rustでは、値の型を指定するためにサフィックスを使用します。データ型を明示的に指定することができます。

let x: i32 = 42;
let y: f64 = 3.14;

型サフィックス一覧

型名 説明
i8, u8 8ビット整数型
i16, u16 16ビット整数型
i32, u32 32ビット整数型
i64, u64 64ビット整数型
i128, u128 128ビット整数型
f32 32ビット浮動小数点型
f64 64ビット浮動小数点型

実用的なエッセンスとテクニック

Rustで長い数値を見やすくする:アンダースコアの活用

Rustでは、長い数値にアンダースコア(_)を挿入することで、桁数が多く視認性が低い数値をより読みやすくすることができます。
// 符号なし8ビット整数 (0から255までの整数) 
let u8_number = 255_u8; 
// 符号付き32ビット整数 (-2,147,483,648から2,147,483,647までの整数) 
let i32_number = -2147_483_648_i32; 
// 符号なし64ビット整数
let u64_number = 18_446_744_073_709_551_615_u64; 
// f32型浮動小数点数 (単精度浮動小数点数) 
let f32_number = 3.14159_265_f32; 
// f64型浮動小数点数 (倍精度浮動小数点数) 
let f64_number = 2.71828_18284_59045_f64; 
// 科学表記法 
let scientific_notation = 1.2345e10; // 12345000000 
// バイナリリテラル 
let binary_number = 0b1111_0000_1111_0000; 
// オクタルリテラル 
let octal_number = 0o777; 

デフォルト型

リテラルのデフォルト型:

型推論が働かない場合、明示的に型を指定する必要があります。

let x = 42;       // デフォルトでi32
let y = 3.14;     // デフォルトでf64
let z: i64 = 100; // 明示的にi64を指定

型の変換

as キーワードを使用して型を変換します。値の範囲や精度に注意が必要です。

let x: i32 = 42;
let y: f64 = x as f64 + 0.5; // i32からf64に変換
let z: u8 = x as u8;         // i32からu8に変換

浮動小数点リテラルの指数表記

指数表記を使用して、非常に大きな値や小さな値を表現できます。

let large = 1.2e10;  // 1.2 x 10^10
let small = 3.4e-5;  // 3.4 x 10^-5

Rustの式の優先順位

Rustでは、式の優先順位が厳密に定義されています。以下の表は、演算子とその優先順位を示しています。

優先順位 カテゴリ 例
1 (最高) 括弧 (expression)
2 メソッド呼び出し x.method(), x[0]
3 一元演算子 -x, !x
4 乗算・除算・剰余演算 *, /, %
5 加算・減算 +, -
6 ビットシフト <<, >>
7 比較演算子 <, >, <=, >=
8 等価演算子 ==, !=
9 ビットAND &
10 ビットXOR ^
11 ビットOR |
12 論理AND &&
13 論理OR ||
14 代入演算子 =, +=, -=, *=

この優先順位に基づいて、Rustコンパイラは複雑な式の解釈順序を決定します。必要に応じて括弧を使用することで、意図した計算順序を強制できます。

let x = 2 + 3 * 4;      // xは14になる (3 * 4が先に計算される)
let y = (2 + 3) * 4;    // yは20になる (2 + 3が先に計算される)

Rust の特長を紹介

 

 

Rustの優れた機能の紹介

1. 所有権システム

Rustの所有権システムにより、所有者が明示され、メモリ管理が厳格に行われます。この仕組みにより、安全なメモリ操作が可能です。

例:


fn main() {
    let s1 = String::from("hello");
    let s2 = s1; // s1の所有権がs2に移動する
    // println!("{}", s1); // コンパイルエラー: s1はもう使用できない
    println!("{}", s2); // "hello" が出力される
}
    

2. 借用チェッカー

借用チェッカーは、データ競合を防ぎ、安全なメモリアクセスを保証します。

例:


fn main() {
    let mut s = String::from("hello");

    let r1 = &s; // 不変借用
    let r2 = &s; // 不変借用
    println!("{} and {}", r1, r2);

    let r3 = &mut s; // 可変借用
    r3.push_str(", world");
    println!("{}", r3); // "hello, world" が出力される
}
    

3. 強力なエラーメッセージ

Rustのコンパイラは親切なエラーメッセージを提供し、問題の原因と解決方法を詳細に示します。

例:


fn main() {
    let x: i32 = "Hello";
}
    
error[E0308]: mismatched types
 --> src/main.rs:2:18
  |
2 |     let x: i32 = "Hello";
  |                  ^^^^^^^ expected `i32`, found `&str`
  |
  = note: expected type `i32`
             found type `&str`
    

4. パターンマッチング

match構文を使うと、複雑な条件分岐を簡潔に記述できます。

例:


let number = 13;

match number {
    1 => println!("One!"),
    2..=12 => println!("Between 2 and 12"),
    _ => println!("Greater than 12"),
}
    

5. 安全な並行プログラミング

所有権と借用システムにより、スレッド間のデータ競合が防がれ、安全な並行プログラミングが可能です。

例:


use std::thread;

fn main() {
    let mut handles = vec![];

    for i in 0..10 {
        let handle = thread::spawn(move || {
            println!("Hello from thread {}", i);
        });
        handles.push(handle);
    }

    for handle in handles {
        handle.join().unwrap();
    }
}
    

6. マクロシステム

macro_rules!やプロシージャルマクロを使って、コードの自動生成や繰り返しを削減できます。

例:


macro_rules! say_hello {
    () => {
        println!("Hello, world!");
    };
}

fn main() {
    say_hello!();
}
    

7. Cargoパッケージマネージャー

Cargoを使って、依存関係の管理、ビルド、テスト、ドキュメント生成を一元的に行えます。

例: Cargo.toml に依存関係を記述する方法


[dependencies]
rand = "0.8"
    

8. Option型とResult型

安全なエラーハンドリングを提供します。

例:


fn divide(a: f64, b: f64) -> Option {
    if b == 0.0 {
        None
    } else {
        Some(a / b)
    }
}

fn main() {
    match divide(4.0, 2.0) {
        Some(result) => println!("Result: {}", result),
        None => println!("Cannot divide by zero"),
    }
}
    

なぜRust を使うのか?

 

Rustの優れた性能とC++との比較

Rustはそのパフォーマンスの高さでも注目されています。C++に匹敵する実行速度を持ちながら、より安全で簡潔な設計を提供する点が特徴です。以下では、Rustの高速性と、C++と比較した際の優位性について詳しく説明します。

Rustの高速なパフォーマンス

Rustは、メモリ安全性を確保しつつ、非常に高いパフォーマンスを誇ります。以下の要素がRustのパフォーマンスを支えています。

  • ゼロコスト抽象化
    Rustは高レベルの抽象化を提供しつつ、その抽象化が実行時のオーバーヘッドを生じさせません。例えば、`Iterator`や`Option`などの型は、高度な機能を提供しながらも、C++に匹敵する速度で動作します。これは、Rustがコンパイル時に最適化を行うため、実行時に余計な負担をかけないからです。
  • 静的メモリ管理
    Rustは所有権システムによって、メモリの管理をコンパイル時に行います。これにより、ガベージコレクションや手動のメモリ解放が必要なく、メモリリークや二重解放のリスクを排除しています。この自動メモリ管理が、パフォーマンスを維持しつつ安全性も担保します。
  • 最適化されたコード生成
    Rustのコンパイラ(`rustc`)は、LLVMをバックエンドとして使用しています。この最適化技術により、生成されるコードは非常に効率的であり、C++に匹敵するパフォーマンスを実現します。
  • 並列処理の最適化
    Rustの所有権システムは並行処理においても非常に効果的です。データ競合を防止する設計により、安全かつ効率的に並列処理を行うことができます。これにより、スレッド間でのデータ共有が非常に効率よく行え、パフォーマンスが向上します。

C++とRustの比較

Rustは、C++の強力な機能を継承しつつ、言語設計が整理されており、使いやすさが向上しています。C++はその複雑な言語仕様から学習コストが高いという課題がありましたが、Rustは次のようにそれを改善しています。

  • メモリ管理
    C++では手動でメモリを管理する必要があり、誤った管理がパフォーマンスや安全性に影響を与えることがあります。一方、Rustは所有権と借用のルールに基づく自動メモリ管理を提供し、メモリ安全性をコンパイル時に確保します。
  • 型システム
    Rustの型システムは、C++よりも厳格であり、コンパイル時に多くのエラーを検出することができます。このシステムにより、実行時エラーを防ぎ、コードの信頼性が向上します。
  • 並行処理の簡素化
    C++ではスレッド管理が複雑であり、競合状態や同期の問題が発生しやすいですが、Rustはデータ競合を防止する仕組みを言語レベルでサポートしており、並行プログラミングを安全かつ簡単に行うことができます。
  • エラー処理
    C++では例外処理を使用しますが、Rustは`Result`型と`Option`型を用いた強力なエラー処理機構を提供し、エラーをより安全かつ効率的に処理します。
  • ライブラリ管理
    C++ではライブラリの管理が個別のプロジェクトごとに行われますが、Rustでは`Cargo`を用いた簡便なパッケージ管理システムを提供し、依存関係やビルドプロセスの管理が自動化されています。

Rustの使いやすさ

Rustは、C++の複雑な仕様を整理し、使いやすさを大幅に向上させました。Rustを使う際に非常に便利な特徴として、次のような点があります。

  • Cargo
    Rustのパッケージ管理ツールであるCargoは、依存関係の管理やビルドプロセスを自動化し、開発者の負担を軽減します。`cargo build`や`cargo run`を使うことで、簡単にプロジェクトを管理できます。
  • コンパイラのエラーメッセージ
    Rustのコンパイラは非常に詳細でわかりやすいエラーメッセージを提供しており、エラーが発生した場合には問題の原因を迅速に特定し、修正を助けてくれます。
  • 標準ライブラリ
    Rustの標準ライブラリは非常に洗練されており、日常的なプログラミングタスクを効率的にこなすためのツールが揃っています。

結論

Rustは、C++の強力な機能を活かしつつ、その複雑さを取り除いて整理された仕様と使いやすさを提供する、次世代のシステムプログラミング言語です。メモリ安全性、並行処理の安全性、そしてパフォーマンスにおいて、C++を超えるポテンシャルを秘めています。これにより、現代のソフトウェア開発において非常に有望な選択肢となっていることは間違いありません。

RustにおけるString型、&str型、char型の違いと比較

 

Rust: String型, &str型, char型の比較

この記事では、Rustで使用される文字列型であるString型、&str型、およびchar型について、その特徴や違いを解説します。

1. 基本的な特徴

Stringåž‹

  • **特徴**: 可変で所有権を持つ文字列型。ヒープ上に格納され、動的に長さを変更可能。
  • **主な用途**: 動的な文字列の管理(追加、削除など)や他のスコープへの所有権の移動。
  • **例**:
    let mut s = String::from("Hello");
    s.push('!'); // 末尾に文字を追加
    println!("{}", s); // 出力: Hello!

&stråž‹

  • **特徴**: 不変で、スライスされた参照型。スタックまたはヒープ上に格納。
  • **主な用途**: 関数への引数として渡す、固定データを操作。
  • **例**:
    let greeting: &str = "Hello, world!"; // スタック上
    println!("{}", greeting);

charåž‹

  • **特徴**: 単一のUnicodeスカラー値を表現。固定サイズ(4バイト)でスタックに格納。
  • **主な用途**: 個別の文字やUnicode文字を扱う場合。
  • **例**:
    let c: char = 'A';
    println!("{}", c); // 出力: A

2. 主な違いの比較

特性 String型 &str型 char型
所有権 あり なし(参照) あり
可変性 可変 不変 不変
用途 動的な文字列操作 参照と固定データの操作 単一文字の処理
メモリ ヒープ スタックまたはヒープ スタック

3. 相互変換

String型 ↔ &str型

let string = String::from("Hello");
let str_slice: &str = &string; // スライス
let new_string: String = str_slice.to_string(); // Stringに変換

&str型 ↔ char型

let s = "Hello";
let first_char: char = s.chars().next().unwrap(); // 最初の文字
let char_as_str: String = first_char.to_string(); // charをStringに変換

String型 ↔ char型

let string = String::from("A");
let char_value: char = string.chars().next().unwrap(); // 最初の文字
let new_string = char_value.to_string(); // charからStringを生成

4. 性能の違い

  • Stringåž‹: 動的操作が可能で便利ですが、メモリの再割り当てが発生する可能性があります。
  • &stråž‹: 軽量で参照型。性能に優れるが不変。
  • charåž‹: 固定サイズで単一の文字に特化。

5. どれを使うべきか?

  • Stringåž‹: 動的操作が必要な場合。
  • &stråž‹: 関数引数や固定データで十分な場合。
  • charåž‹: 個別の文字やUnicode処理。

これらの特徴を活かし、適切な型を選ぶことで効率的なRustプログラムを構築できます。

Rustのchar型メソッド

 

Rustのchar型メソッド完全ガイド

この記事では、Rustで提供されるchar型のすべてのメソッドについて解説します。これにより、文字操作を効率的に行えるようになります。

基本情報の取得

  • len_utf8() - UTF-8としてエンコードする際のバイト数を返します。
  • len_utf16() - UTF-16としてエンコードする際のバイト数を返します。
  • is_alphabetic() - アルファベットかどうかを判定します。
  • is_numeric() - 数字かどうかを判定します。
  • is_alphanumeric() - アルファベットまたは数字かどうかを判定します。
  • is_lowercase() - 小文字かどうかを判定します。
  • is_uppercase() - 大文字かどうかを判定します。
  • is_whitespace() - 空白文字かどうかを判定します。
  • is_control() - 制御文字かどうかを判定します。
  • is_ascii() - ASCII文字かどうかを判定します。
  • その他のASCII関連メソッド:is_ascii_alphabetic(), is_ascii_digit(), など。

変換

  • to_ascii_uppercase() - ASCII大文字に変換します。
  • to_ascii_lowercase() - ASCII小文字に変換します。
  • to_digit(radix: u32) - 指定した基数でcharを数字として解釈します。
  • to_uppercase() - Unicodeに基づいて大文字に変換します。
  • to_lowercase() - Unicodeに基づいて小文字に変換します。

エスケープ

  • escape_default() - デフォルトのエスケープ文字列を生成します。
  • escape_unicode() - Unicodeエスケープ文字列を生成します。
  • escape_debug() - デバッグ用のエスケープ文字列を生成します。

ユーティリティ

  • from_u32(u32) -> Option - Unicodeスカラー値からcharを生成します。
  • from_u32_unchecked(u32) - 範囲チェックなしでcharを生成します(unsafe)。

使用例

fn main() {
    let c = 'A';

    // 基本情報の取得
    println!("UTF-8のバイト数: {}", c.len_utf8());
    println!("アルファベットか: {}", c.is_alphabetic());
    println!("大文字か: {}", c.is_uppercase());

    // 変換
    println!("小文字に変換: {}", c.to_lowercase());
    println!("16進数として解釈: {:?}", c.to_digit(16));

    // エスケープ
    println!("デフォルトエスケープ: {}", c.escape_default());
    println!("Unicodeエスケープ: {}", c.escape_unicode());
}

実行結果

UTF-8のバイト数: 1
アルファベットか: true
大文字か: true
小文字に変換: a
16進数として解釈: Some(10)
デフォルトエスケープ: A
Unicodeエスケープ: \u{41}

さらに詳細を知りたい場合は、公式ドキュメントを確認してください。

Rust の&strのメソッド一覧

 

Rustの&strメソッド完全ガイド

Rustの文字列スライス型&strには、豊富なメソッドが用意されています。この記事では、&str型のすべてのメソッドについて、カテゴリごとに解説します。

基本操作

  • len() - バイト数を取得します。
  • is_empty() - 文字列が空かどうかを判定します。

変換

  • as_bytes() - 文字列をバイト列 (&[u8]) に変換します。
  • chars() - Unicodeスカラ値ごとのイテレータを返します。
  • char_indices() - バイト位置と文字のペアを返すイテレータ。
  • graphemes() - グラフェム単位で分割するイテレータ (要クレート unicode-segmentation)。
  • lines() - 改行で区切られた各行をイテレータとして返します。

検索

  • contains(&str) - 部分文字列を含むかどうかを判定します。
  • find(&str) - 部分文字列の最初の位置を返します。
  • rfind(&str) - 部分文字列の最後の位置を返します。
  • starts_with(&str) - 指定の部分文字列で始まるか判定します。
  • ends_with(&str) - 指定の部分文字列で終わるか判定します。
  • matches(&str) - 部分文字列に一致するすべての位置をイテレータとして返します。
  • rmatches(&str) - 部分文字列に一致する位置を後方から探索するイテレータ。
  • match_indices(&str) - 部分文字列とその位置のペアをイテレータとして返します。
  • rmatch_indices(&str) - 後方からの一致位置と部分文字列のペアを返します。

置換

  • replace(&str, &str) - 部分文字列をすべて置き換えます。
  • replacen(&str, &str, usize) - 指定回数だけ部分文字列を置き換えます。
  • replace_range(Range<usize>, &str) - 指定範囲の文字列を置き換えます (要 &mut String)。

分割

トリミング

  • trim() - 前後の空白を削除します。
  • trim_start() - 先頭の空白を削除します。
  • trim_end() - 末尾の空白を削除します。
  • trim_matches(&str | char | Fn(char) -> bool) - 条件に一致する文字を前後から削除します。
  • trim_start_matches(&str | char | Fn(char) -> bool) - 条件に一致する文字を先頭から削除します。
  • trim_end_matches(&str | char | Fn(char) -> bool) - 条件に一致する文字を末尾から削除します。

大文字と小文字の変換

  • to_lowercase() - 小文字に変換します。
  • to_uppercase() - 大文字に変換します。

反復

  • repeat(usize) - 文字列を指定回数繰り返します。
  • drain(Range<usize>) - 指定範囲を削除し、削除された部分を返します (要 &mut String)。

パース

  • parse<T>() - 文字列を任意の型 T に変換します。

位置と範囲

  • get(Range) - 指定範囲のスライスを取得します。
  • get_mut(Range) - 指定範囲の可変スライスを取得します (要 &mut String)。
  • get_unchecked(Range) - 範囲チェックなしでスライスを取得します (unsafe)。
  • get_unchecked_mut(Range) - 範囲チェックなしで可変スライスを取得します (unsafe)。
  • slice(Range) - 範囲内の文字列スライスを取得します。
  • slice_mut(Range) - 範囲内の可変スライスを取得します (要 &mut String)。
  • strip_prefix(&str) - 指定した接頭辞を削除します。
  • strip_suffix(&str) - 指定した接尾辞を削除します。
  • strip_suffix_unchecked(&str) - 範囲チェックなしで接尾辞を削除します (unsafe)。

その他の便利なメソッド

  • escape_default() - 非ASCII文字や制御文字をエスケープします。
  • escape_debug() - デバッグ用のエスケープ形式を生成します。
  • escape_unicode() - Unicodeエスケープ形式を生成します。
  • from_utf8(&[u8]) - バイト列から&strを生成します。
  • from_utf8_unchecked(&[u8]) - UTF-8チェックなしでバイト列から&strを生成します (unsafe)。
  • from_utf16(&[u16]) - UTF-16バイト列から文字列を生成します。
  • from_utf16_lossy(&[u16]) - 無効な文字を置換しつつUTF-16から文字列を生成します。

 

参考リンク:

Rust メモ リテラル - エンジニアですよ!

Rust メモ 文字列 - エンジニアですよ!

RustのString型で使用可能な主なメソッド

 

RustのString型で使えるメソッド一覧

RustのString型で使用可能なメソッドを、用途ごとに分類してまとめました。

1. 文字列操作

文字列の変更

  • push(char): 一文字を追加。
  • push_str(&str): 文字列スライスを末尾に追加。
  • insert(usize, char): 指定位置に一文字挿入。
  • insert_str(usize, &str): 指定位置に文字列スライスを挿入。
  • replace(&str, &str): 指定部分を置換して新しいStringを返す。
  • truncate(usize): 指定長さに切り詰め。
  • clear(): 文字列を空にする。

削除

  • pop(): 末尾の一文字を取り除いて返す。
  • remove(usize): 指定位置の一文字を削除。
  • drain(Range): 指定範囲を削除しつつ取り出す。

2. 文字列検索

部分文字列の検索

  • find(&str): 部分文字列の開始位置を返す。
  • rfind(&str): 後ろから検索して部分文字列の位置を返す。
  • contains(&str): 指定の部分文字列が含まれるか判定。
  • starts_with(&str): 指定文字列で始まるか判定。
  • ends_with(&str): 指定文字列で終わるか判定。

3. スライス・切り出し

部分文字列の抽出

  • get(Range): 文字列スライスを取得。
  • get_mut(Range): 可変な文字列スライスを取得。

分割

  • split(&str): 指定文字列で分割。
  • split_whitespace(): 空白文字で分割。
  • lines(): 行ごとに分割。

4. 長さ・容量管理

長さとバイト数

  • len(): 文字列のバイト長を返す。
  • is_empty(): 空文字列か判定。

容量管理

  • capacity(): 確保されているバイト数を返す。
  • reserve(usize): 追加容量を確保。
  • shrink_to_fit(): 未使用容量を削減。

5. 変換

  • as_str(): &strとして借用を取得。
  • to_lowercase(): 小文字に変換。
  • to_uppercase(): 大文字に変換。

6. イテレーション

  • chars(): 各文字を反復。
  • bytes(): 各バイトを反復。

7. その他のユーティリティ

  • eq_ignore_ascii_case(&str): ASCII大文字小文字を無視して比較。

さらに詳しくは、公式ドキュメントを参照してください。