【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】ヒープ領域

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

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

【Rust】ベクタ(vector)

ベクタは型の異なる要素を含めることはできない。要素数は可変。インデックスに変数を使用することができる。

fn main(){
    let mut vect = vec![10, 20, 30];
    vect.push(40);
    println!("{} {} {} {}", vect[0], vect[1], vect[2], vect[3]);

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

C++

#include <vector>
#include <stdio.h>

int main(void)
{
    std::vector<int> vec{1,2,3};

    for(int i = 0; i < vec.size(); i++)
        printf("%d", vec[i]);
    printf("\n");

    return 0;
}
#include <iostream>
#include <string>
#include <vector>

int main()
{
    std::vector<std::string> words = {"apple", "bird", "cat"};

    std::cout << words.size() << '\n';

    for(const auto& word : words)
    {
        std::cout << word << ' ';
    }

    std::cout << '\n';
}

rustの場合はsizeではなくlenで書く。

fn main(){
    let words = vec!["apple", "bird", "cat"];

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

    for w in &words {
        println!("{}", w);
    }
}

【Rust】配列

配列はTという単一の型のオブジェクトの集合。それらのオブジェクトはメモリ上の連続した領域に保存される。

fn main(){
    let arr = [10, 20, 30];
    println!("{}, {}, {}", arr[0], arr[1], arr[2]);

    for v in &arr {
        println!{"{}", v};
    }
}

C++の配列

#include <iostream>

void PrintArray1(const int x[5]){
    static_assert(sizeof(x) == sizeof(int*), "");
    for (int i = 0; i < 5; ++i){
        std::cout << x[i] << std::endl;
    }
}

void PrintArray2(const int* x) {
    for (int i = 0; i < 5; ++i){
        std::cout << x[i] << std::endl;
    }
}

int main()
{
    int x[5] = {0, 1, 2, 3, 4};

    PrintArray1(x);
    PrintArray2(x);

    return 0;
}

↓ Rustで書くことこうなる

fn PrintArray1(ary: [i32; 5]) {
    for i in ary {
        println!{"{}", i};
    }
}

fn main(){
    let x: [i32; 5]= [0, 1, 2, 3, 4];

    PrintArray1(x);
}

関数の引数は明確に定義しないといけない。

【Rust】タプル

タプルは異なる型の値の集合。括弧を用いて生成する。

fn main(){
    let tup = (10, "20", 30);
    println!("{}, {}, {}", tup.0, tup.1, tup.2);
}

C++でタプルを使用する場合は、型を指定しなければいけないが、Rustの場合は型指定がない。つまり自動で型推論をやってくれている?

#include <iostream>
#include <string>
#include <tuple>
#include <vector>

int main()
{
    std::tuple<int, double, std::string> t1{100, 1.1, "aaa"};
    std::tuple<std::string, int, int> t2{"bbb", 200, 300};
    std::vector<std::tuple<std::string, int, int>> v =
    {
        {"ccc", 400, 500},
        {"ddd", 600, 700}
    };
}

Rustではインデックスを用いて値にアクセスできるが、以下のような書き方をするとエラーとなる。

fn main(){
    let tup = (1, 1.2, "tupple");
    for n in 0..2 {
        println!("{}", tup.n);
    }
}

$ rustc main.rs
error[E0609]: no field `n` on type `({integer}, {float}, &str)`
–> main.rs:4:28
|
4 | println!(“{}”, tup.n);
| ^ unknown field

error: aborting due to 1 previous error

For more information about this error, try `rustc –explain E0609`.

また、型推論できない場合は、型を明示する必要がある。

【Rust】列挙型

### 定数値として扱う

enum Color {
    Red = 0x0000FF,
    Green = 0x00FF00,
    Blue = 0xFF0000,
}
fn main(){
    let color = Color::Red;
    println!("{}", color as usize);
}

### 特定の値を指定しない時

enum Color {
    Red,
    Green,
    Blue,
}
fn main(){
    let color = Color::Red as usize;
    println!("{}", color);
}

### 値を持つ列挙型

enum Color {
    RGB(u8, u8, u8),
    RGBA(u8, u8, u8, u8),
}
fn main(){
    let c = Color::RGB(0, 0, 255);
    let code = match c {
        Color::RGB(r, g, b) => b as u32 + ((g as u32) << 8) + ((r as u32) << 16),
        Color::RGBA(r, g, b, _) => b as u32 + ((g as u32) << 8) +  ((r as u32) << 16),   
    };
    println!("{}", code);
}

### 値を持つenumを比較

#[derive(Debug,PartialEq)]
enum Animal {
    Dog(String),
    Cat(String),
    Monkey(String),
}

fn main(){
    let taro1 = Animal::Dog(String::from("Taro"));
    let taro2 = Animal::Dog(String::from("Taro"));
    assert_eq!(taro1, taro2);

    let taro3 = Animal::Cat(String::from("Taro"));
    let taro4 = Animal::Dog(String::from("Taro"));
    assert_eq!(taro3, taro4);

    let taro5 = Animal::Monkey(String::from("Taro"));
    let taro6 = Animal::Monkey(String::from("Taro"));
    // println!(taro5 as usize, taro6 as usize);
}

値を持つ、持たないで扱いが異なるのね。。
列挙型と構造体は何となく似ているが、列挙型は名前を列挙していくだけなので、型指定はしない。

【Rust】構造体と共用体

### 構造体

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

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

### 共用体
共用体は同じメモリを使用する

union MyUnion {
    f1: u32,
    f2: u32,
}

fn main() {
    let u = MyUnion {f1: 123};
    unsafe {
        println!("{}", u.f1);
        println!("{}", u.f2);
    }
}

$ ./main
123
123

【Rust】Ubuntu24.04にRustをインストールする

### rustが入っていない状態
$ cargo –version
bash: cargo: command not found

### インストール
$ cd $home
$ sudo apt-get update
$ sudo apt-get install libssl-dev libudev-dev pkg-config zlib1g-dev llvm clang cmake make libprotobuf-dev protobuf-compiler
$ curl –proto ‘=https’ –tlsv1.2 -sSf https://sh.rustup.rs | sh
$ source $HOME/.cargo/env

### 最新バージョンの確認
$ rustup component add rustfmt
$ rustup update

### インストール確認
$ cargo –version
cargo 1.83.0 (5ffbef321 2024-10-29)
$ rustc –version
rustc 1.83.0 (90b35a623 2024-11-26)