チュートリアル

Rust基礎:関数

Rust入門関数
広告エリア

関数の定義

// 基本的な関数
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でクロージャの特性を指定
  • ジェネリクスで型パラメータを使用

次回は配列とコレクションについて学びます。

広告エリア