Rust 2015 と Rust 2018 の違い
loop の戻り値
Rust 2018以降、loopブロック内でbreakに値を渡すことで、ループ全体の戻り値を直接変数に代入できます。
// 従来の書き方
let x;
loop {
x = 7;
break;
}
// 新しい書き方(戻り値を直接代入)
let x = loop { break 7; };
impl Trait 構文
引数や戻り値の型としてトレイトを直接指定する場合にimpl Traitを使用します。これにより、ボックス化や明示的なジェネリクス記述を省略できます。
// 引数の位置
trait MyTrait {}
fn example_fn<T: MyTrait>(arg: T) {} // 従来
fn example_fn(arg: impl MyTrait) {} // 新しい構文
// 戻り値の位置
impl MyTrait for i32 {}
fn return_trait_object() -> Box<dyn MyTrait> { // 従来
Box::new(5)
}
fn return_trait_object() -> impl MyTrait { // 新しい構文
5
}
// クロージャの戻り値
fn return_closure() -> Box<dyn Fn(i32) -> i32> { // 従来
Box::new(|x| x + 1)
}
fn return_closure() -> impl Fn(i32) -> i32 { // 新しい構文
|x| x + 1
}
dyn Trait 構文
トレイトオブジェクトを明示するためにdynキーワードを使用します。
trait MyTrait {}
impl MyTrait for i32 {}
// 従来
fn old_func() -> Box<MyTrait> { /* ... */ }
// 新しい構文(dynを明示)
fn new_func() -> Box<dyn MyTrait> { /* ... */ }
use std::rc::Rc;
trait Foo {}
impl Foo for i32 {}
fn main() {
let instance: Rc<dyn Foo> = Rc::new(5);
}
関連関数と関連定数
他の言語でいう静的メソッドや静的定数に相当する機能です。
// 関連関数の例
struct Data;
impl Data {
fn describe() {
println!("これはData型の関連関数です");
}
}
fn main() {
Data::describe();
}
// 構造体の関連定数
struct Config;
impl Config {
const VERSION: u32 = 1;
}
fn main() {
println!("バージョン: {}", Config::VERSION);
}
// トレイトの関連定数
trait Identifiable {
const ID: u32;
}
struct User;
impl Identifiable for User {
const ID: u32 = 42;
}
fn main() {
println!("ユーザーID: {}", User::ID);
}
閉区間演算子 (..=)
範囲の終端を含む閉区間を表現するために..=を使用します。
// 半開区間(1, 2)
for i in 1..3 {
println!("値: {}", i);
}
// 閉区間(1, 2, 3)
for i in 1..=3 {
println!("値: {}", i);
}
非レキシカルライフタイム
Rust 2018では、ライフタイムの推論が改善され、不変参照の使用が終わった後であれば可変参照が作成可能になりました。
fn main() {
let mut value = 5;
let borrow = &value; // 不変参照
let mut_borrow = &mut value; // 可変参照(コンパイル可能)
println!("borrow: {}", borrow); // エラー:不変参照は既に無効
}
? 演算子によるエラーハンドリング
?演算子を使用することで、エラー処理を簡潔に記述できます。
use std::io;
fn get_username() -> Result<String, io::Error> {
// 従来のmatchを使用した方法
let file = match File::open("user.txt") {
Ok(f) => f,
Err(e) => return Err(e),
};
let mut content = String::new();
match file.read_to_string(&mut content) {
Ok(_) => Ok(content),
Err(e) => Err(e),
}
}
// try!マクロを使用した方法(旧版)
fn get_username_try() -> Result<String, io::Error> {
let mut file = try!(File::open("user.txt"));
let mut content = String::new();
try!(file.read_to_string(&mut content));
Ok(content)
}
// ?演算子を使用した新しい方法
fn get_username_short() -> Result<String, io::Error> {
let mut file = File::open("user.txt")?;
let mut content = String::new();
file.read_to_string(&mut content)?;
Ok(content)
}
スライスパターン
スライスに対してパターンマッチングを行い、要素数に応じた処理を実装できます。
fn greet(group: &[&str]) {
match group {
[] => println!("誰もいませんね…"),
[one] => println!("やあ、{}さん。一人ですか?", one),
[first, second] => println!(
"{}さんと{}さん、こんにちは。少なくとも2人はいますね!",
first, second
),
_ => println!("皆さん、今日は{}人いますね。", group.len()),
}
}
fn main() {
greet(&[]);
greet(&["太郎"]);
greet(&["花子", "次郎"]);
greet(&["一郎", "二郎", "三郎"]);
}
let numbers = [10, 20, 30];
assert_eq!("末尾は30", match numbers {
[_, _, 30] => "末尾は30",
[a, b, c] => "その他のパターン",
});