関数の定義
// 基本的な関数
fn greet() {
println!("こんにちは!");
}
// 引数あり
fn greet_person(name: &str) {
println!("こんにちは、{}さん!", name);
}
// 戻り値あり
fn add(a: i32, b: i32) -> i32 {
a + b // returnは省略可能(最後の式が戻り値)
}
// 明示的なreturn
fn divide(a: i32, b: i32) -> Option<i32> {
if b == 0 {
return None;
}
Some(a / b)
}
fn main() {
greet();
greet_person("太郎");
let sum = add(3, 5);
println!("合計: {}", sum);
}
引数
参照渡し
// 不変参照
fn print_length(s: &String) {
println!("長さ: {}", s.len());
}
// 可変参照
fn append_world(s: &mut String) {
s.push_str(", World!");
}
fn main() {
let mut text = String::from("Hello");
print_length(&text); // 借用
append_world(&mut text); // 可変借用
println!("{}", text); // "Hello, World!"
}
スライス
fn first_word(s: &str) -> &str {
let bytes = s.as_bytes();
for (i, &byte) in bytes.iter().enumerate() {
if byte == b' ' {
return &s[..i];
}
}
s
}
fn sum(numbers: &[i32]) -> i32 {
numbers.iter().sum()
}
fn main() {
let sentence = "Hello World";
println!("{}", first_word(sentence)); // "Hello"
let nums = [1, 2, 3, 4, 5];
println!("{}", sum(&nums)); // 15
}
戻り値
複数の値を返す(タプル)
fn min_max(numbers: &[i32]) -> (i32, i32) {
let min = *numbers.iter().min().unwrap();
let max = *numbers.iter().max().unwrap();
(min, max)
}
fn main() {
let numbers = [3, 1, 4, 1, 5, 9, 2, 6];
let (min, max) = min_max(&numbers);
println!("最小: {}, 最大: {}", min, max);
}
Result を返す
use std::num::ParseIntError;
fn parse_number(s: &str) -> Result<i32, ParseIntError> {
s.parse::<i32>()
}
fn main() {
match parse_number("42") {
Ok(n) => println!("数値: {}", n),
Err(e) => println!("エラー: {}", e),
}
// ?演算子でエラー伝播
fn add_numbers(a: &str, b: &str) -> Result<i32, ParseIntError> {
let a = a.parse::<i32>()?;
let b = b.parse::<i32>()?;
Ok(a + b)
}
}
クロージャ
// 基本
let add = |a, b| a + b;
println!("{}", add(3, 5));
// 型注釈付き
let multiply: fn(i32, i32) -> i32 = |a, b| a * b;
// 環境をキャプチャ
let factor = 2;
let double = |x| x * factor;
println!("{}", double(5)); // 10
// moveキーワード
let text = String::from("Hello");
let print_text = move || println!("{}", text);
print_text();
// println!("{}", text); // エラー: 所有権が移動済み
クロージャと高階関数
let numbers = vec![1, 2, 3, 4, 5];
// map
let doubled: Vec<i32> = numbers.iter().map(|x| x * 2).collect();
// filter
let evens: Vec<&i32> = numbers.iter().filter(|x| *x % 2 == 0).collect();
// find
let first_even = numbers.iter().find(|x| *x % 2 == 0);
// any / all
let has_even = numbers.iter().any(|x| x % 2 == 0);
let all_positive = numbers.iter().all(|x| *x > 0);
// fold
let sum = numbers.iter().fold(0, |acc, x| acc + x);
ジェネリック関数
// ジェネリック関数
fn largest<T: PartialOrd>(list: &[T]) -> &T {
let mut largest = &list[0];
for item in list {
if item > largest {
largest = item;
}
}
largest
}
fn main() {
let numbers = vec![34, 50, 25, 100, 65];
println!("最大: {}", largest(&numbers));
let chars = vec!['y', 'm', 'a', 'q'];
println!("最大: {}", largest(&chars));
}
where句
fn print_debug<T>(value: T)
where
T: std::fmt::Debug,
{
println!("{:?}", value);
}
fn compare_and_display<T, U>(t: T, u: U)
where
T: std::fmt::Display + Clone,
U: std::fmt::Debug,
{
println!("t: {}, u: {:?}", t, u);
}
関数ポインタ
fn add(a: i32, b: i32) -> i32 {
a + b
}
fn apply(f: fn(i32, i32) -> i32, a: i32, b: i32) -> i32 {
f(a, b)
}
fn main() {
let result = apply(add, 3, 5);
println!("{}", result); // 8
// クロージャも渡せる
let result = apply(|a, b| a * b, 3, 5);
println!("{}", result); // 15
}
クロージャを引数に取る
// Fn - 不変参照でキャプチャ
fn call_with_one<F>(f: F) -> i32
where
F: Fn(i32) -> i32,
{
f(1)
}
// FnMut - 可変参照でキャプチャ
fn apply_twice<F>(mut f: F, value: i32) -> i32
where
F: FnMut(i32) -> i32,
{
let v1 = f(value);
f(v1)
}
// FnOnce - 所有権を取得
fn consume<F>(f: F)
where
F: FnOnce(),
{
f();
}
実践例:バリデーション
type Validator<T> = fn(&T) -> Result<(), String>;
fn validate<T>(value: &T, validators: &[Validator<T>]) -> Result<(), Vec<String>> {
let errors: Vec<String> = validators
.iter()
.filter_map(|v| v(value).err())
.collect();
if errors.is_empty() {
Ok(())
} else {
Err(errors)
}
}
fn not_empty(s: &String) -> Result<(), String> {
if s.is_empty() {
Err("空文字は許可されません".to_string())
} else {
Ok(())
}
}
fn min_length(min: usize) -> Validator<String> {
move |s: &String| {
if s.len() < min {
Err(format!("{}文字以上必要です", min))
} else {
Ok(())
}
}
}
fn main() {
let validators: Vec<Validator<String>> = vec![
not_empty,
min_length(3),
];
let name = String::from("ab");
match validate(&name, &validators) {
Ok(()) => println!("有効"),
Err(errors) => {
for e in errors {
println!("エラー: {}", e);
}
}
}
}
まとめ
fn name(args) -> ReturnTypeで関数を定義- 最後の式が戻り値(
returnは省略可能) &Tで不変参照、&mut Tで可変参照?演算子でエラー伝播- クロージャは
|args| expression Fn,FnMut,FnOnceでクロージャの特性を指定- ジェネリクスで型パラメータを使用
次回は配列とコレクションについて学びます。