【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);
}

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

【Rust】関数

fn add(x: i32, y: i32) -> i32 {
    return x + y;
}

fn main(){
    let r = add(4, 7);
    println!("{}", r);
}

C++で関数を使って書いてみる

#include <iostream>
#include <stdio.h>
#include <string>
#include <map>

int total(std::map<std::string, int> &cart){
    int price = 0;
    for(auto i : cart){
        price = price + i.second;
    }
    std::cout << "※合計: " << price << "円" << std::endl;
    return price;
}

int tax(int price) {
    int tax_price = price * 0.1;
    std::cout << "※税: " << tax_price << "円" << std::endl;
    return tax_price;
}

void cashier(std::map<std::string, int> &cart) {
    for(auto i: cart) {
        std::cout << i.first << ": " << i.second << "円" << std::endl;
    }

    int price = total(cart);
    int tax_price = tax(price);
    
    std::cout << "お会計: " << price + tax_price << "円" << std::endl;
}

int main()
{
    std::map<std::string, int> cart;

    cart["Crab mayoo"] = 2280;
    cart["Salad"] = 780;
    cart["Cola"] = 190;

    cashier(cart);
    
    return 0;
}

上記をRustで記述する。

use std::collections::HashMap;

fn total(cart :HashMap<&str, u32> )-> u32 {
    let mut price: u32 = 0;
    for (key, value) in cart.iter() {
        price = price + value;
        println!("{} : {}円", key, value);
    }
    println!("※合計 {}円", price);
    return price;
}

fn tax(price: u32) -> u32 {
    let tax: u32 = price / 10;
    println!("※税 {}円", tax);
    return tax;
}

fn cashier(cart :HashMap<&str, u32>) {
    let price: u32 = total(cart);
    let tax_price = tax(price);
    println!("合計 {}円", price + tax_price);
}

fn main(){
    let mut cart = HashMap::new();
    cart.insert("Crab mayoo", 2280);
    cart.insert("Salad", 780);
    cart.insert("Cola", 190);

    cashier(cart);
}

なるほど、u32 * floatはできないので、price * 0.1とするとエラーになる。
Rustは型指定が結構大変だなぁ

【Rust】スライス


fn main(){
    let s = String::from("ABCDEFGH");
    let s1 = &s[0..3];
    let s2 = &s[3..6];
    println!("{} {}", s1, s2);


}

c++でsliceはないので、関数で書いている場合

#include <iostream>
#include <vector>

template<typename T>
void print(std::vector<T> const &v)
{
    for (auto i: v) {
        std::cout << i << ' ';
    }
    std::cout << std::endl;
}

template<typename T>
std::vector<T> slice(std::vector<T> const &v, int m, int n)
{
    auto first = v.cbegin() + m;
    auto last = v.cbegin() + n + 1;

    std::vector<T> vec(first, last);
    return vec;
}

int main()
{
    std::vector<int> v = {1, 2, 3, 4, 2, 2, 4, 6, 5};

    int m = 4, n = 7;
    std::vector<int> sub_vec = slice(v, m, n);
    print(sub_vec);
    return 0;
}
fn print(v: Vec<i32>) {
    for &i in & v {
        print!("{} ", &i);
    }
    print!("\n");
}

fn slicing(v: Vec<i32>, m: i32, n: i32) -> Vec<i32>
{
    let vec = &v[m..n];
    return vec.to_vec();
}

fn main(){
    let mut vect = vec![1, 2, 3, 4, 2, 2, 4, 6, 5];
    let v = slicing(vect, 4, 7);
    print(v);
}

>> slice indices are of type `usize` or ranges of `usize`

これでなぜ引数i32がエラーになるか理解できん…

【Rust】ヒープ領域

ヒープ領域とは、プログラム実行時に動的にメモリを割り当てるための領域
ヒープ領域は関数が終わっても存在することができ、所有者が居なくなった時点で解放される

struct Point {
    x: i32,
    y: i32,
}

fn main(){
    let p: Box<Point> = Box::new(Point {x:100, y:200});
    println!("{} {}", p.x, p.y);
}

new演算子、std::vectorはヒープ領域を使用している。

#include <iostream>
#include <limits>
#include <exception>

int main()
{
    std::cout << std::hex << std::numeric_limits<uintptr_t>::max() << std::end;

    try {
        char* temp= new char[std::numeric_limits<uintptr_t>::max()];
        std::cout << temp << "\n";
    }
    catch(std::bad_alloc& e){
        std::cout << e.what() << "\n";
    }

    return 0;
}
#include <iostream>
#include <limits>
#include <exception>

int main()
{
    int* array = new int[5];

    for(int i = 0; i < 5; ++i){
        array[i] = i*i;
    }

    delete[] array;
    array = nullptr;
    return 0;
}

いまいちboxの使い所と書き方がわかりませんな…

【Rust】文字列

Rustの基本的な文字列型は&strを使用する。

fn main(){
    let mut name: &str = "Yamada";

    println!("{}", name);

    name = "Tanaka";

    println!("{}", name);
}

ライブラリとして提供されている文字列にStringがある。

fn main(){
    let mut name = String::from("Yamada");

    name = "Tanaka".to_string();
    name.push_str(" Taro");
    println!("{}", name);
}

C++のchar型は1文字のみ

#include <stdio.h>

int main(void)
{
    char a = 'D';
    char b = 'N';
    char c = 'A';

    printf("%c%c%c\n", a, b, c);
    return 0;

}

複数文字は配列

#include <stdio.h>

int main(void)
{
    char str1[] = "DNA";

    printf("%s\n", str1);
    printf("%lu\n", sizeof(str1));

    return 0;

}

char型のポインタ

int main(void)
{
    const char* char_ptr = "DNA";

    printf("%s\n", char_ptr);

    return 0;

}

string型

#include <stdio.h>
#include <string>
#include <iostream>

int main(void)
{
    std::string str1 = "DNA";
    std::string str2 = "RNA";
    std::string str3 = str1 + str2;

    std::cout << str3 << std::endl;

    return 0;

}
fn main(){
    let s1 = String::from("ABC");
    let s2 = String::from("ABC");
    let s3 = String::from("XY");

    if(s1 == s2){
        println!("ABC == ABC");
    }
    if(s1 < s3) {
        println!("ABC < XY");
    }
}

String::fromが便利すぎる。。

【Rust】ハッシュマップ(Hashmap)

ハッシュマップは文字列を添字に使用することができる。他の言語では連想配列と呼ばれる。

use std::collections::HashMap;

fn main(){
    let mut map = HashMap::new();
    map.insert("x", 10);
    map.insert("y", 20);
    map.insert("z", 30);

    println!("{} {} {}", map["x"], map["y"], map["z"]);

    for (k, v) in &map {
        println!("{} {}", k, v);
    }
}

C++

#include <iostream>
#include <map>
#include <string>

using namespace std;

int main()
{
    map<int, string> Players;

    Players.insert(std::pair<int, string>(2, "Lin Dan"));
    Players.insert(std::pair<int, string>(1, "Chen Long"));

    cout << "Number of Players " << Players.size() << endl;
    for(map<int, string>::iterator it = Players.begin(); it != Players.end();
        ++it){
            cout << (*it).first << ": " << (*it).second << endl;
        }

}
use std::collections::HashMap;

fn main(){
    let mut Players = HashMap::new();
    Players.insert(2, "Lin Dan");
    Players.insert(1, "Chen Long");

    println!("{}", Players.len());

    for (k, v) in &Players {
        println!("{}: {}", k, v);
    }
}

$ ./main
2
1: Chen Long
2: Lin Dan