【Rust】モジュール(mood, pub, use, as)

modはモジュールを使用することを宣言する。pubはモジュール外からもその名前にアクセスするために必要。

foo.rs

pub fn foo_func() {
    println!("Foo!");
}

main.rs

mod foo;

fn main() {
    foo::foo_func();
}

先ほどの例を記述すると
foo.rs

pub fn add(a: f32, b: f32) -> f32 {
    a + b
}

pub fn subtract(a: f32, b: f32) -> f32 {
    a - b
}

pub fn multiply(a: f32, b: f32) -> f32 {
    a * b
}

pub fn divide(a: f32, b: f32) -> f32 {
    a / b
}

main.rs

mod foo;

fn main() {
    let a: f32 = 7.4;
    let b: f32 = 99.0;

    let x = foo::add(a, b);
    println!("a + b = {}", x);
    let y = foo::subtract(a, b);
    println!("a - b = {}", y);
    let z = foo::multiply(a, b);
    println!("a * b = {}", z);
    let d = foo::divide(a, b);
    println!("a / d = {}", d);
}

$ cargo run
Compiling hoge v0.1.0 (/home/vagrant/dev/rust/hoge)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.97s
Running `target/debug/hoge`
a + b = 106.4
a – b = -91.6
a * b = 732.60004
a / d = 0.07474747

ヘッダーファイルが無い分、C++よりスッキリしてますね。。

【Rust】Cargoの使い方とcrate(クレート)

### cargoがインストールされていることを確認
$ cargo –version
cargo 1.83.0 (5ffbef321 2024-10-29)

## プロジェクトの作成
$ cargo new hoge

main.rs

use rand::Rng;

fn main() {
    let mut rng = rand::thread_rng();
    for _i in 1..10 {
        println!("{}", rng.gen_range(1, 101));
    }
}

Cargo.tom

[package]
name = "hoge"
version = "0.1.0"
edition = "2021"

[dependencies]
rand = "0.7"

$ cargo run
26
4
61
71
29
17
33
37
59

### C++でライブラリを作る
MathLibrary.h

#pragma once

namespace MathLibrary
{
    class Arithmetic
    {
    public:
        static double Add(double a, double b);

        static double Subtract(double a, double b);

        static double Multiply(double a, double b);

        static double Divide(double a, double b);
    };
}
#include "MathLibrary.h"

namespace MathLibrary
{
    double Arithmetic::Add(double a, double b)
    {
        return a + b;
    }

    double Arithmetic::Subtract(double a, double b)
    {
        return a - b;
    }

    double Arithmetic::Multiply(double a, double b)
    {
        return a * b;
    }

    double Arithmetic::Divide(double a, double b)
    {
        return a / b;
    }


}
#include <iostream>
#include "MathLibrary.h"

int main() {
    double a = 7.4;
    int b = 99;

    std::cout << "a + b = " <<
        MathLibrary::Arithmetic::Add(a, b) << std::endl;
    std::cout << "a - b = " <<
        MathLibrary::Arithmetic::Subtract(a, b) << std::endl;
    std::cout << "a * b = " <<
        MathLibrary::Arithmetic::Multiply(a, b) << std::endl;
    std::cout << "a / b = " <<
        MathLibrary::Arithmetic::Add(a, b) << std::endl;
}

$ g++ -o test MathLibrary.cpp test.cpp && ./test
a + b = 106.4
a – b = -91.6
a * b = 732.6
a / b = 106.4

crateというより、複数ファイルの使用か…

【Rust】非同期関数(async, await)

use futures::executor::block_on;

struct Song {
    lyric: String,
}

async fn learn_and_sing() {
    let song = learn_song().await;
    sing_song(song).await;
}

async fn learn_song() -> Song {
    let song = Song { lyric: String::from("La la la...") }
    println!("Learned song");
    return song;
}

async fn sing_song(song: Song) {{
    println!("{}", song.lyric);
}}

async fn dance() {
    println!("Dance");
}

async fn async_main(){
    let f1 = learn_and_sing();
    let f2 = dance();
    futures::join!(f1, f2);
}

fn main(){
    block_on(async_main());
}

to use `async fn`, switch to Rust 2018 or later
2015 Rustだと使えない模様

c++の非同期処理

#include <iostream>
#include <future>

int main() {
    std::promise<int> pr;
    std::future<int> ft = pr.get_future();
    pr.set_value(100);
    int i = ft.get();
    std::cout << i << std::endl;
}

【Rust】マルチスレッド

thread::spawn関数で新規スレッドを生成する。

use std::thread;
use std::time::Duration;

fn main(){
    let th = thread::spawn (|| {
        for _i in 1..10 {
            println!("A");
            thread::sleep(Duration::from_millis(500));
        }
    });
    th.join().unwrap();
    println!("Finished");
}

スレッドからスレッド外変数を参照するにはmoveを使用する。

use std::thread;
use std::time::Duration;

fn main(){
    let str = String::from("ABC");
    let th = thread::spawn (move|| {
        for _i in 1..10 {
            println!("{}", str);
            thread::sleep(Duration::from_millis(100));
        }
    });
    th.join().unwrap();
    println!("Finished");
}

C/C++ で新しいスレッドを立ち上げる

#include <thread>
#include <cstdio>
#include <cstdint>

uint32_t end_flag_;
uint32_t msg_;

void ThreadA()
{
    int32_t input = 0;
    while(end_flag_){
        printf("数字を入力してください\n");
        printf("0...スレッドBにメッセージを送信します\n");
        printf("1...プロセスを終了します\n");
        scanf("%d", &input);

        switch(input){
            case 0:
                msg_ = 1;
                break;
            case 1:
                end_flag_ = 0;
                break;
            default:
                printf("0か1を入力してください。\n");
                break;
        }
    }
    printf("スレッドA終了\n");
}

void ThreadB()
{
    while(end_flag_){
        if(msg_){
            printf("スレッドAからメッセージを受信しました\n");
            msg_ = 0;
        }
    }
    printf("スレッドB終了\n");
}

int main() {
    msg_ = 0;
    end_flag_ = 1;

    std::thread th_a(ThreadA);
    std::thread th_b(ThreadB);

    th_a.join();
    th_b.join();

    return 0;
}

Rustで書いてみる

use std::thread;

fn main(){
    let mut msg_ : u32 = 0;
    let mut end_flag_ : u32 = 1;

    let thA = thread::spawn (move|| {
        while end_flag_ != 0 {
            println!("数字を入力してください");
            println!("1...スレッドBにメッセージを送信します");
            println!("2...プロセスを終了します");
            let mut word = String::new();
            std::io::stdin().read_line(&mut word).ok();
            let num: i32 = word.trim().parse().ok().unwrap();
            println!("入力値: {}", num);
            match num {
                1 => { msg_ = 1}
                2 => { end_flag_ = 0}
                _ => println!("0か1を入力してください"),
            }
        }
    });

    let thB = thread::spawn (move|| {
        while end_flag_ != 0 {
            if msg_ == 1 {
                println!("スレッドAからメッセージを受信しました。");
                msg_ = 0;
            }
        }
    });
    thA.join().unwrap();
    thB.join().unwrap();
}

スレッドは二つ立っているんだけど、moveで所有権が移転すると、変数msg_がもう読み込まれなくなる。なるほど、難しいね。

【Rust】コンソールから数字入力を取得する

fn main(){
    println!("数字を入力してください");
    println!("0...スレッドBにメッセージを送信します");
    println!("1...プロセスを終了します");
    let mut word = String::new();
    std::io::stdin().read_line(&mut word).ok();
    let num: i32 = word.trim().parse().ok().unwrap();
    println!("{}", num);
}

$ ./main
数字を入力してください
0…スレッドBにメッセージを送信します
1…プロセスを終了します
1
1

word.trim().parse().ok().unwrap(); のところで、word.parse().unwrap(); で記述すると、”thread ‘main’ panicked at main.rs:10:33:”のエラーが出てしまう。。
なかなか難しいですな。。

【Rust】イテレータ(iterator)

トレイトを実装するオブジェクトをイテレータと呼ぶ
イテレータとは、連続したオブジェクトを順番に取り扱うための機能を提供するオブジェクト

pub trait Iterator {
    type Item;
    fn next(&mut self) -> Option<Self::Item>;
    
    // methods with default implementations elided
}

イテレータにはmap、filter、collectなどのメソッドがある。

fn main(){
    let vec = vec![1, 2, 3, 4, 5];
    let mut iter = vec.iter();

    while let Some(item) = iter.next() {
        println!("{}", item);
    }
}
struct Counter {
    max: u32,
    count: u32,
}

impl Counter {
    fn new(max: u32) -> Counter {
        Counter {max: max, count: 0}
    }
}

impl Iterator for Counter {
    type Item = u32;
    fn next(&mut self) -> Option<Self::Item>{
        self.count += 1;
        if self.count < self.max {
            Some(self.count)
        } else {
            None
        }
    }
}

fn main(){
    let counter = Counter::new(10);
    for c in counter {
        println!("{}", c);
    }
}

C++

#include <bits/stdc++.h>
using namespace std;

int main() {
    vector<int>a = {1, 2, 3, 5};
    auto itr = find(a.begin(), a.end(), 3);

    if (itr == a.end()) {
        cout << "not found" << endl;
    } else {
        int idx = distance(a.begin(), itr);
        cout << "a[" << idx << "] =" << *itr << endl;
    }
}
fn main(){
    let a = vec![1, 2, 4, 6, 8];
    let mut iter = a.iter();

    let mut counter = 0;
    while let Some(item) = iter.next() {
        counter = counter + 1;
        if *item == 3 {
            println!("idx {}", counter);
            break;
        } 
        if counter == a.len() {
            println!("not found");
        }
    }
}

毎回、もうちょっと上手い書き方が絶対にあると思う。。

【Rust】トレイト

Rustのトレイトとは共通の振る舞いを定義すること。
トレイトとはデータ型を分類する仕組み。
トレイト内に共通のメソッドを定義する。

struct Rect { width: u32, height: u32 }

trait Printable { fn print(&self); }
impl Printable for Rect {
    fn print(&self){
        println!("width:{}, height:{}", self.width, self.height)
    }
}

fn main(){
    let r = Rect { width: 200, height: 300 };
    r.print();
}
struct Rect<T> { width: T, height: T }

trait Printable { fn print(&self); }
impl<T> Printable for Rect<T> where T: std::fmt::Display {
    fn print(self: &Rect<T>){
        println!("width:{}, height:{}", self.width, self.height);
    }
}

fn main(){
    let r1: Rect<i32> = Rect { width: 200, height: 300 };
    let r2: Rect<i64> = Rect { width: 200, height: 300 };
    r1.print();
    r2.print();
}
use std::boxed::Box;

struct Dog {}
struct Cat {}
trait Animal { fn cry(&self); }
impl Animal for Dog { fn cry(&self) {println!("Bow-wow");}}
impl Animal for Cat { fn cry(&self) {println!("Miaow");}}

fn get_animal(animal_type: &str) -> Box<dyn Animal> {
    if animal_type == "dog" {
        return Box::new(Dog {});
    } else {
        return Box::new(Cat {});
    }
} 

fn main(){
    get_animal("dog").cry();
    get_animal("cat").cry();
}

C++のテンプレート

#include <iostream>
#include <cmath>

struct Point {
    double x, y;
};


template <typename T>
class DistanceCalculator {
public:
    double calculateDistance(const T& point1, const T& point2) {
        return std::sqrt(std::pow(point2.x - point1.x, 2) + std::pow(point2.y - point1.y, 2));
    }
};

auto main(void) -> int {
    Point p1 = {1.0, 2.0};
    Point p2 = {4.0, 6.0};

    DistanceCalculator<Point> calculator;
    std::cout << "Distance: " << calculator.calculateDistance(p1, p2) << std::endl;

    return 0;
}

$ g++ -o test test.cpp && ./test
Distance: 5

struct Point<T> {
    x1: T, y1: T, x2: T, y2: T,
}

trait DistanceCalculator { fn calculate(&self); }
impl<T> DistanceCalculator for Point<T> where T: std::fmt::Display {
    fn calculate(&self) {
        let d = ((self.x2 - self.x1).pow(2) +  (self.y2 - self.y1).pow(2)).sqrt();
        println!("{}", d);
    }
}

fn main(){
    let p = Point {x1:1, y1:4, x2: 2, y2: 6};
    p.calculate();
}

cannot subtract `T` from `T`

struct Point {
    x: i32, y: i32
}

impl Point {
    fn calculate(p1:Point, p2:Point) {
        let d = ((p2.x - p1.x).pow(2) +  (p2.y - p1.y).pow(2)).sqrt();
        println!("{}", d);
    }
}

fn main(){
    let p1 = Point {x: 1, y: 4};
    let p2 = Point {x: 2, y: 6};
    calculate(p1, p2);
}

うーん、いまいちわからん…

【Rust】インプリメンテーション(impl)

インプリメンテーションとは構造体にメソッドを追加するもの。Rustにクラスはない。

struct Rect { width: u32, height: u32}

impl Rect {
    fn area(&self) -> u32 {
        self.width * self.height
    }
}

fn main(){
    let r = Rect {width: 200, height: 300};
    println!("{}", r.area());
}

C++のclass

#include <iostream>
#include <string>

class Car {
    public:
        std::string owner;
        std::string color;
        int number;
};

auto main(void) -> int {
    auto a = Car();
    a.owner = "山田";
    a.color = "blue";
    a.number = 1234;
    std::cout << "a.owner =" << a.owner << ", a.color =" << a.color 
        << ", a.number =" << a.number << std::endl;
}

rustでimplを使ってclassを書く

struct Car { 
    owner: String, 
    color: String,
    number: u32,
}

impl Car {
    fn prnt(&self) {
        println!("{} {} {}", self.owner, self.color, self.number);
    }
}

fn main(){
    let c = Car {owner: "山田".to_string(), color: "blue".to_string(), number: 1234};
    c.prnt();
}

$ ./main
山田 blue 1234

なるほど〜

【Rust】マクロ

マクロの基本ルール

macro_rules! foo {
    () => ()
}

foo!();

exprは式。マッチャーにはitem, block, stmt, pat, ty, ident, path, tt, metaなどがある。

macro_rules! log {
    ($x:expr) => { println!("{}", $x);}
}

fn main(){
    log!("ABC...");
}

### c言語のマクロ
defineとはプリプロセッサへの指示内容の一つ。

#include <stdio.h>

#define PI 3.14f
#define CIRCLE_RATIO "円周率"

int main(void)
{
    double area = PI * 10 * 10;
    char *ptr = CIRCLE_RATIO;

    printf("%sは: %f", ptr, PI);
    printf("半径10の円の面積は: %f\n", area);

    return 0;
}
#include <stdio.h>

#define PI 3.14
#define area(r)(PI*r*r)
#define prt(f) printf("%f\n", f)

int main(void)
{
    prt(area(10.0f));

    return 0;
}
macro_rules! area {
    ($e1:expr, $e2:expr) => {  $e1 * $e2 * $e2}
}

macro_rules! prnt {
    ($e:expr) => { println!("{}", $e);}
}

fn main(){
    let PI : f32 = 3.14;
    let result = area!(PI, 5.0);
    prnt!(result);
}

関数ではなくわざわざマクロを使うメリットがいまいちイメージが掴めないが機能的に便利そうではある。

【Rust】クロージャー

変数に保存したり、引数として他の関数に渡すことができる匿名関数。無名関数やラムダ式に似ている。

fn main(){
    let square = | x: i32 | {
        x * x
    };
    println!("{}", square(9));
}

moveでクロージャー外変数をクロージャー内に移動することができる。

fn main(){
    let msg = String::from("Hello");
    let func = move || {
        println!("{}", msg);
    };
    func();
}
#include <iostream>

auto func() {
    int x = 0;
    return [=]() mutable -> void {
        ++x;
        std::cout << x << std::endl;
    };
}

int main()
{
    auto f1 = func();
    f1();
    f1();
    f1();
    f1();

    auto f2 = func();
    f2();
    f2();
    f2();
    f2();
}
fn main(){
    let mut i = 0;
    let make_counter = | mut x:i32 | {
        i = x + 1;
        println!("{}", i);
    };

    make_counter(i);
    make_counter(i);
}

クロージャの中でグローバル変数は
3 | let make_counter = | mut x:i32 | {
| ————- `i` is borrowed here
4 | i = x + 1;
| – borrow occurs due to use of `i` in closure

fn main(){
    let mut i: i32 = 1;
    let make_counter = | mut x:i32 | {
        x = x + 1;
        println!("{}", x);
        return x;
    };

    i = make_counter(i);
    i = make_counter(i);
}

スマートな書き方ではありませんね。。
クロージャの値をうまく保持し続けたい時の書き方が知りたい。。