Rust
#13
TIOBE#18
PYPL#8
GitHub#25
RedMonk#19
IEEESpectrum#11
JetBrains#14
プログラミング言語
Rust
概要
Rustは、メモリ安全性と高性能を両立するシステムプログラミング言語です。
詳細
Rustは2010年にMozillaのGraydon Hoareによって開発が始まった、システムプログラミング言語です。最大の特徴は、ガベージコレクションを使わずにメモリ安全性を保証する所有権システムです。C/C++のような高性能を維持しながら、メモリリーク、バッファオーバーフロー、データ競合などの問題を コンパイル時に防ぐことができます。Webブラウザエンジン、オペレーティングシステム、WebAssembly、暗号通貨、ゲームエンジンなど、パフォーマンスが重要な分野で採用が進んでいます。近年ではLinuxカーネル開発でも採用が始まっています。
書き方の例
Hello World
// 基本的な出力
fn main() {
println!("Hello, World!");
// 変数を使った出力
let message = "こんにちは、Rust!";
println!("{}", message);
// フォーマット文字列を使った出力
let name = "太郎";
let age = 25;
println!("私の名前は{}で、{}歳です。", name, age);
}
変数と所有権
fn main() {
// 不変変数(デフォルト)
let name = "太郎";
let age = 25;
// 可変変数
let mut score = 85;
score = 90; // 変更可能
// 型注釈
let height: f64 = 175.5;
let is_active: bool = true;
// 所有権の移動
let s1 = String::from("Hello");
let s2 = s1; // s1の所有権がs2に移動
// println!("{}", s1); // エラー: s1は使用できない
println!("{}", s2);
// クローン(複製)
let s3 = String::from("World");
let s4 = s3.clone(); // s3を複製
println!("{} {}", s3, s4); // 両方使用可能
// 借用(参照)
let s5 = String::from("Rust");
let len = calculate_length(&s5); // 借用
println!("'{}' の長さは {} です。", s5, len); // s5は使用可能
}
fn calculate_length(s: &String) -> usize {
s.len() // 借用なので所有権は移動しない
}
データ型とコレクション
fn main() {
// 基本型
let integer: i32 = 42;
let float: f64 = 3.14;
let character: char = '🦀';
let boolean: bool = true;
// タプル
let person: (String, i32, f64) = (String::from("田中"), 30, 175.5);
let (name, age, height) = person; // 分解
println!("名前: {}, 年齢: {}, 身長: {}cm", name, age, height);
// 配列(固定長)
let numbers: [i32; 5] = [1, 2, 3, 4, 5];
let first = numbers[0];
// ベクター(可変長)
let mut fruits = vec!["りんご", "バナナ", "オレンジ"];
fruits.push("ぶどう"); // 要素の追加
// ハッシュマップ
use std::collections::HashMap;
let mut scores = HashMap::new();
scores.insert(String::from("数学"), 85);
scores.insert(String::from("英語"), 92);
scores.insert(String::from("国語"), 78);
// パターンマッチングでアクセス
match scores.get("数学") {
Some(score) => println!("数学の点数: {}", score),
None => println!("数学の点数が見つかりません"),
}
println!("果物: {:?}", fruits);
println!("点数: {:?}", scores);
}
構造体とメソッド
// 構造体の定義
#[derive(Debug)] // デバッグ出力を可能にする
struct Person {
name: String,
age: u32,
email: String,
}
// メソッドの実装
impl Person {
// 関連関数(コンストラクタ的な役割)
fn new(name: String, age: u32, email: String) -> Person {
Person { name, age, email }
}
// メソッド(&self を使用)
fn get_info(&self) -> String {
format!("{}({}歳)- {}", self.name, self.age, self.email)
}
// 可変メソッド(&mut self を使用)
fn set_age(&mut self, new_age: u32) {
self.age = new_age;
}
// 所有権を取るメソッド(self を使用)
fn into_email(self) -> String {
self.email
}
}
// タプル構造体
struct Point(f64, f64);
impl Point {
fn distance_from_origin(&self) -> f64 {
(self.0 * self.0 + self.1 * self.1).sqrt()
}
}
fn main() {
// 構造体のインスタンス作成
let mut person = Person::new(
String::from("田中太郎"),
25,
String::from("[email protected]"),
);
println!("{}", person.get_info());
// 可変メソッドの呼び出し
person.set_age(26);
println!("更新後: {}", person.get_info());
// タプル構造体
let point = Point(3.0, 4.0);
println!("原点からの距離: {:.2}", point.distance_from_origin());
// デバッグ出力
println!("デバッグ: {:?}", person);
}
列挙型とパターンマッチング
// 列挙型の定義
#[derive(Debug)]
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}
// Option型の使用例
fn find_word(text: &str, word: &str) -> Option<usize> {
text.find(word)
}
// Result型の使用例
fn divide(a: f64, b: f64) -> Result<f64, String> {
if b == 0.0 {
Err(String::from("ゼロで除算はできません"))
} else {
Ok(a / b)
}
}
impl Message {
fn call(&self) {
match self {
Message::Quit => println!("プログラムを終了します"),
Message::Move { x, y } => println!("({}, {}) に移動します", x, y),
Message::Write(text) => println!("メッセージ: {}", text),
Message::ChangeColor(r, g, b) => {
println!("色を RGB({}, {}, {}) に変更します", r, g, b)
}
}
}
}
fn main() {
// 列挙型の使用
let messages = vec![
Message::Move { x: 10, y: 20 },
Message::Write(String::from("Hello, Rust!")),
Message::ChangeColor(255, 0, 0),
Message::Quit,
];
for message in messages {
message.call();
}
// Option型の使用
let text = "Hello, Rust programming!";
match find_word(text, "Rust") {
Some(index) => println!("'Rust' は位置 {} にあります", index),
None => println!("'Rust' が見つかりません"),
}
// if letによる簡潔なパターンマッチング
if let Some(index) = find_word(text, "programming") {
println!("'programming' は位置 {} にあります", index);
}
// Result型の使用
match divide(10.0, 3.0) {
Ok(result) => println!("10.0 ÷ 3.0 = {:.2}", result),
Err(error) => println!("エラー: {}", error),
}
// ? 演算子による簡潔なエラーハンドリング
if let Err(e) = try_division() {
println!("計算エラー: {}", e);
}
}
fn try_division() -> Result<(), String> {
let result = divide(5.0, 0.0)?; // エラーの場合は早期リターン
println!("結果: {}", result);
Ok(())
}
トレイト(インターフェース)
// トレイトの定義
trait Summary {
fn summarize(&self) -> String;
// デフォルト実装
fn announce(&self) {
println!("要約: {}", self.summarize());
}
}
// 構造体の定義
struct NewsArticle {
headline: String,
location: String,
author: String,
content: String,
}
struct Tweet {
username: String,
content: String,
reply: bool,
retweet: bool,
}
// トレイトの実装
impl Summary for NewsArticle {
fn summarize(&self) -> String {
format!("{} by {} ({})", self.headline, self.author, self.location)
}
}
impl Summary for Tweet {
fn summarize(&self) -> String {
format!("{}: {}", self.username, self.content)
}
}
// トレイト境界を使った関数
fn notify(item: &impl Summary) {
println!("速報: {}", item.summarize());
}
// 複数のトレイト境界
use std::fmt::Display;
fn some_function<T>(item: &T) -> String
where
T: Summary + Display,
{
format!("表示: {} | 要約: {}", item, item.summarize())
}
// 標準ライブラリのトレイトを実装
impl Display for Tweet {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Tweet by {}: {}", self.username, self.content)
}
}
fn main() {
let article = NewsArticle {
headline: String::from("Rustプログラミング言語が人気上昇"),
location: String::from("東京"),
author: String::from("田中太郎"),
content: String::from("Rustの採用が増えています..."),
};
let tweet = Tweet {
username: String::from("rust_lover"),
content: String::from("Rustでプログラミング中!"),
reply: false,
retweet: false,
};
// トレイトメソッドの呼び出し
article.announce();
tweet.announce();
// トレイト境界を使った関数の呼び出し
notify(&article);
notify(&tweet);
// 複数のトレイト境界
println!("{}", some_function(&tweet));
}
ライフタイム
// ライフタイム注釈
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
// 構造体でのライフタイム
struct ImportantExcerpt<'a> {
part: &'a str,
}
impl<'a> ImportantExcerpt<'a> {
fn level(&self) -> i32 {
3
}
// ライフタイム省略規則により'aは自動的に推論される
fn announce_and_return_part(&self, announcement: &str) -> &str {
println!("ご注意ください: {}", announcement);
self.part
}
}
fn main() {
let string1 = String::from("長い文字列です");
{
let string2 = String::from("短い");
let result = longest(string1.as_str(), string2.as_str());
println!("最長の文字列: {}", result);
}
// 構造体でのライフタイム使用
let novel = String::from("昔々、ある所に。その先の物語は...");
let first_sentence = novel.split('.').next().expect("'.'が見つかりませんでした");
let excerpt = ImportantExcerpt {
part: first_sentence,
};
println!("重要な抜粋: {}", excerpt.part);
let announcement = "新しい章が始まります";
println!("戻り値: {}", excerpt.announce_and_return_part(announcement));
}
並行プログラミング
use std::thread;
use std::time::Duration;
use std::sync::mpsc;
use std::sync::{Arc, Mutex};
fn main() {
// 基本的なスレッド
let handle = thread::spawn(|| {
for i in 1..10 {
println!("スレッドから: {}", i);
thread::sleep(Duration::from_millis(1));
}
});
for i in 1..5 {
println!("メインから: {}", i);
thread::sleep(Duration::from_millis(1));
}
handle.join().unwrap(); // スレッドの完了を待機
// チャネルを使った通信
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
let vals = vec![
String::from("こんにちは"),
String::from("スレッド"),
String::from("から"),
String::from("です"),
];
for val in vals {
tx.send(val).unwrap();
thread::sleep(Duration::from_secs(1));
}
});
for received in rx {
println!("受信: {}", received);
}
// 共有状態(Mutex)
let counter = Arc::new(Mutex::new(0));
let mut handles = vec![];
for _ in 0..10 {
let counter = Arc::clone(&counter);
let handle = thread::spawn(move || {
let mut num = counter.lock().unwrap();
*num += 1;
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
println!("結果: {}", *counter.lock().unwrap());
}
エラーハンドリングとテスト
// カスタムエラー型
use std::fmt;
use std::error::Error;
#[derive(Debug)]
struct CustomError {
message: String,
}
impl fmt::Display for CustomError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "カスタムエラー: {}", self.message)
}
}
impl Error for CustomError {}
// エラーを返す関数
fn divide_numbers(a: f64, b: f64) -> Result<f64, Box<dyn Error>> {
if b == 0.0 {
Err(Box::new(CustomError {
message: String::from("ゼロで除算はできません"),
}))
} else {
Ok(a / b)
}
}
// テスト関数
pub fn add_two(a: i32) -> i32 {
a + 2
}
fn main() {
// エラーハンドリングの例
match divide_numbers(10.0, 2.0) {
Ok(result) => println!("10 ÷ 2 = {}", result),
Err(e) => println!("エラー: {}", e),
}
match divide_numbers(10.0, 0.0) {
Ok(result) => println!("10 ÷ 0 = {}", result),
Err(e) => println!("エラー: {}", e),
}
// パニックの例(プログラムが停止する)
// panic!("これはパニックです!");
}
// テストモジュール
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_adds_two() {
assert_eq!(4, add_two(2));
}
#[test]
fn test_divide_numbers() {
assert_eq!(divide_numbers(10.0, 2.0).unwrap(), 5.0);
assert!(divide_numbers(10.0, 0.0).is_err());
}
#[test]
#[should_panic]
fn test_panic() {
panic!("このテストはパニックするはずです");
}
}
バージョン
バージョン | リリース日 | 主な新機能 |
---|---|---|
Rust 1.81 | 2024-09 | Error trait in core, Lint against ambiguous wide pointer comparisons |
Rust 1.80 | 2024-07 | LazyCell and LazyLock, Exclusive ranges in patterns |
Rust 1.79 | 2024-06 | Inline const expressions, Bounds in associated type position |
Rust 1.78 | 2024-05 | Diagnostic attributes, assert_unsafe_precondition |
Rust 1.77 | 2024-03 | C-string literals, Offset of struct fields |
Rust 1.76 | 2024-02 | Arc::unwrap_or_clone, Inspection functions on Option and Result |
Rust 1.75 | 2023-12 | async fn and return-position impl trait in traits |
参考ページ
公式ドキュメント
- Rust公式サイト - 公式サイト
- The Rust Programming Language - Rustの教科書
- Rust by Example - 実例によるRust学習
学習リソース
- Rustlings - Rust学習用エクササイズ
- Rust Reference - Rust言語リファレンス
- The Cargo Book - Cargoパッケージマネージャーガイド
開発ツール
- crates.io - Rustパッケージレジストリ
- Rust Playground - ブラウザ上でRustを試せる環境
- Awesome Rust - Rustライブラリ・ツール集