【Rust】Tokioとは?

Node.jsとGolangを合体させたもの
膨大なリクエストをシングルスレッド&イベントループ&非同期I/Oで効率よく処理するランタイム(ライブラリ)
tokioではOSではなくランタイムがタスクのスケジューリングを行う。タスク生成はtokio::spawnを呼び出す

use std::time::Duration;

#[tokio::main]
async fn main() {
    tokio::task::spawn(async {
        tokio::time::sleep(Duration::from_secs(2)).await;
        println!("Done");
    });

    std::thread::sleep(Duration::from_secs(3));
    println!("wake up");
}

シングルスレッド

use std::time::Duration;

#[tokio::main(flavor="current_thread")]
async fn main() {
    tokio::task::spawn(async {
        tokio::time::sleep(Duration::from_secs(2)).await;
        println!("Done");
    });

    std::thread::sleep(Duration::from_secs(3));
    println!("wake up");
}
use std::time::Duration;

#[tokio::main(flavor="multi_thread", worker_threads = 1)]
async fn main() {
    let cpus = num_cpus::get();
    println!("logical cores: {}", cpus);

    tokio::task::spawn(async move {
        println!("task 1 started..");
        tokio::time::sleep(Duration::from_secs(3)).await;
        println!("task 1 wake up");
    });

    tokio::task::spawn(async move {
        println!("task 2 started..");
        tokio::time::sleep(Duration::from_secs(1)).await;
        println!("task 2 wake up");
    });

    tokio::time::sleep(Duration::from_secs(5)).await;
    println!("wake up");
}

task 1 started..
task 2 started..
task 2 wake up
task 1 wake up
wake up

use std::time::Duration;
use futures::{stream::FuturesUnordered, StreamExt};
use std::thread;

#[tokio::main(flavor="multi_thread", worker_threads = 1)]
async fn main() {
    // console_subscriber::init();

    let mut tasks = FuturesUnordered::new();

    let h1 = tokio::spawn(async move {
        thread::sleep(Duration::from_secs(10)).await;
        println!("0 wake up");
    });
    tasks.push(h1);
    println!("0 started...");

    for i in 1..100 {
        let h2 = tokio::spawn(async move {
            tokio::time::sleep(Duration::from_secs(1)).await;
            println!("{i} woke up!");
        });
        tasks.push(h2);
        println!("{i} ...");
    }
    while let Some(item) = tasks.next().await {
        let () = item.unwrap();
    }
    println!("Done");
}

【Rust】axumでパスパラメータを取得して使用する

Postではなく、Getでデータを渡したいときに、パスパラメータを使用する
route(“/withdrawal/{param}” でパスパラメータを取得できる。以前は、route(“/withdrawal/:param” だったので変わったみたい。

html

<button onclick="location.href='./withdrawal/eyJ2ZXJzaW9uIjoiMC4xLjAiLCJ0aW1lIjoiMjAyNS0wMi0xNVQwNzoxNzo0Mi4xMDU3Njk4NDNaIiwic2VuZGVyIjoiMDQxMGY4NzQyOWU4OTQ5OGU5MjhkMDBiNmE2MTg2ZmRjOWNjYmRlMmNhNTVkNTc0NmYwZTFiZjhmMWExZmJkYjc'">ボタン</button>

axum

    let app = Router::new()
        .route("/", get(handle_index))
        .route("/withdrawal/{param}", get(handle_withdrawal))

//

async fn handle_withdrawal(axum::extract::Path(param): axum::extract::Path<String>) {

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

なるほど〜

【Rust】WebAssemblyを使用する

$ cargo install wasm-pack
$ cargo new –lib hello-wasm
$ tree
.
└── hello-wasm
├── Cargo.toml
└── src
└── lib.rs

2 directories, 2 files

src/lib.rs

use wasm_bindgen::prelude::*;

#[wasm_bindgen]
extern {
    pub fn alert(s: &str);
}

#[wasm_bindgen]
pub fn greet(name: &str){
    alert(&format!("Hello, {}!", name));
}

Cargo.toml

[lib]
crate-type = ["cdylib"]

[dependencies]
wasm-bindgen= "0.2"

build
$ wasm-pack build –target web

index.html

<body>
    <script type="module">
        import init, { greet } from "./pkg/hello_wasm.js";
        init().then(() => {
            greet("WebAssembly");
        });
    </script>
</body>

なるほど、WebAssemblyか、次々に新しい技術が出てきますね…

参考)
https://developer.mozilla.org/ja/docs/WebAssembly/Guides/Rust_to_Wasm

【Rust】ワークスペースによるパッケージ管理

$ mkdir add
$ cd add

Cargo.toml

[workspace]

members = [
    "adder",
]

$ cargo new –bin adder
$ tree
.
├── adder
│ ├── Cargo.toml
│ └── src
│ └── main.rs
└── Cargo.toml

$ cargo new add-one –lib
$ tree
.
├── adder
│ ├── Cargo.toml
│ └── src
│ └── main.rs
├── add-one
│ ├── Cargo.toml
│ └── src
│ └── lib.rs
└── Cargo.toml

pub fn add_one(x: i32) -> i32 {
    x + 1
}

adder/Cargo.toml

[dependencies]
add-one = {path = "../add-one"}

adder/src/main.rs

extern crate add_one;

fn main() {
    let num = 10;
    println!("Hello, world!{} plus one is {}!", num, add_one::add_one(num));
}

$ cargo run
Compiling add-one v0.1.0 (/home/vagrant/dev/rust/add/add-one)
Compiling adder v0.1.0 (/home/vagrant/dev/rust/add/adder)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.15s
Running `target/debug/adder`
Hello, world!10 plus one is 11!

なるほど、ワークスペースを使ってパッケージ管理してるのか!

【Rust】非同期処理 Async, awaitを整理する

プリエンティブマルチタスク: OSにより実行を制御する
協調的マルチタスク: コンテキストの切り替え、スレッドの置き換えはできる
コールチン: 協調的マルチタスクを制御できる
Futureオブジェクト: 処理が終わったらコールバック、awaitは実行完了まで待つ

use std::{thread, time};
#[tokio::main]
async fn main(){

    let handle = tokio::spawn(async {
        thread::sleep(time::Duration::from_millis(1000));
        println!("1 sec elapsed");
    });
    
    let order = "beef burger".to_string();
    order_burger(order).await;
    play_game().await;
}

async fn order_burger(order: String) -> Result<String, Box<dyn std::error::Error>> {
    match order {
        ref val if *val == "beef burger".to_string() => thread::sleep(time::Duration::from_millis(200)),
        ref val if *val == "cake".to_string()  => thread::sleep(time::Duration::from_millis(300)),
        _ => println!("Please order!"),
    }
    println!("Order received {}", order);
    Ok(order)
}

async fn play_game() {
    println!("I am playing game");
}

Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.60s
Running `target/debug/app`
Order received beef burger
I am playing game
1 sec elapsed

asyncの関数をawaitで実行した場合は、その次の関数は実行完了まで実行されない。
別のスレッドを立てた場合は、並列で処理がされる。

なるほど、async処理をawaitする意味が何となくわかった。

【Rust】WebSocketのClient側の送信

https://docs.rs/websockets/latest/websockets/struct.WebSocket.html
websockets=”0.3.0″

use websockets::WebSocket;
use websockets::Frame;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut ws = WebSocket::connect("wss://echo.websocket.org/").await?;
    ws.send_text("foo".to_string()).await?;
    ws.receive().await?;
    if let Frame::Text { payload: received_msg, .. } =  ws.receive().await? {
        println!("{}", received_msg);
    }
    ws.close(None).await?;
    Ok(())
}   

Compiling app v0.1.0 (/home/vagrant/dev/rust/app)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 2.00s
Running `target/debug/app`
foo

なるほどー

【Rust】関数にi32を渡して、i32の値を更新して返却する

関数外部で宣言した値は関数内部では更新できないので、引数に渡して、返却することで実質的に更新してることになる。

fn sub(i: u32) -> u32{
    return i + 1;
}

fn main() {
    let mut i = 1;
    for n in 0..5 {
        i = sub(i);
    }
    println!("{}", i);
}

Running `target/debug/sample`
6

この原理を応用する

fn build_layer(&mut self,n: usize) -> usize{
        let mut num = n;
        let mut new_layer: Vec<Node> = Vec::new();

        if self.layer.len() % 2 == 1 {
            self.layer.push(self.layer[self.layer.len() - 1].clone());
            self.leaves.push(self.leaves[self.leaves.len() - 1].clone());
        }

        for i in (0..self.layer.len()).step_by(2) {
            let parent_data = sha256hash(self.layer[i].data.clone(), self.layer[i+1].data.clone());
            let left = Node {left: "".to_string(), right: "".to_string(), parent: parent_data.clone(), sibling: self.layer[i+1].data.clone(), position: Position::Left, data: self.layer[i].data.clone(), hash:hash(self.layer[i].data.clone())};
            let right = Node {left: "".to_string(), right: "".to_string(), parent: parent_data.clone(), sibling: self.layer[i].data.clone(), position: Position::Right, data: self.layer[i+1].data.clone(), hash:hash(self.layer[i+1].data.clone())};
            let parent = Node {left: self.layer[i].data.clone(), right: self.layer[i+1].data.clone(), parent: "".to_string(), sibling: "".to_string(), position: Position::None, data: parent_data.clone(), hash:hash(parent_data.clone())};
            new_layer.push(parent.clone());
            self.leaves[num] = left.clone();
            self.leaves[num+1] = right.clone();
            
            self.leaves.push(parent.clone());
            num = num + 2;
        }
        self.layer = new_layer;
        return num;
    }

    fn build_tree(&mut self) {
        let mut n = 0;
        while self.layer.len() > 1 {
            n = self.build_layer(n);
        }
        self.root = self.layer[0].data.clone();
    }

[Node { left: “”, right: “”, parent: “2ce109e9d0faf820b2434e166297934e6177b65ab9951dbc3e204cad4689b39c”, sibling: “bbb”, position: Left, data: “aaa”, hash: “9834876dcfb05cb167a5c24953eba58c4ac89b1adf57f28f2f9d09af107ee8f0” }, Node { left: “”, right: “”, parent: “2ce109e9d0faf820b2434e166297934e6177b65ab9951dbc3e204cad4689b39c”, sibling: “aaa”, position: Right, data: “bbb”, hash: “3e744b9dc39389baf0c5a0660589b8402f3dbb49b89b3e75f2c9355852a3c677” }, Node { left: “”, right: “”, parent: “fcb0de60e5febc4dea96c4dc0153362d289f1aa5bd0797db2dcd7f23708205bb”, sibling: “ddd”, position: Left, data: “ccc”, hash: “64daa44ad493ff28a96effab6e77f1732a3d97d83241581b37dbd70a7a4900fe” }, Node { left: “”, right: “”, parent: “fcb0de60e5febc4dea96c4dc0153362d289f1aa5bd0797db2dcd7f23708205bb”, sibling: “ccc”, position: Right, data: “ddd”, hash: “730f75dafd73e047b86acb2dbd74e75dcb93272fa084a9082848f2341aa1abb6” }, Node { left: “”, right: “”, parent: “b0f69b8d46703ad68753ec87d319678be392a9e2fa024449750821f886e5d1af”, sibling: “fff”, position: Left, data: “eee”, hash: “282b91e08fd50a38f030dbbdee7898d36dd523605d94d9dd6e50b298e47844be” }, Node { left: “”, right: “”, parent: “b0f69b8d46703ad68753ec87d319678be392a9e2fa024449750821f886e5d1af”, sibling: “eee”, position: Right, data: “fff”, hash: “f284bdc3c1c9e24a494e285cb387c69510f28de51c15bb93179d9c7f28705398” }, Node { left: “”, right: “”, parent: “d5fe46b1204cb7bb339b929b1777e04d2c7235915d7a50d6baa134e20dbc80c6”, sibling: “gggg”, position: Left, data: “gggg”, hash: “45d25abffe8c792d74d30346429b5bc244b815eeb378a9c38395f7a466cf6894” }, Node { left: “”, right: “”, parent: “d5fe46b1204cb7bb339b929b1777e04d2c7235915d7a50d6baa134e20dbc80c6”, sibling: “gggg”, position: Right, data: “gggg”, hash: “45d25abffe8c792d74d30346429b5bc244b815eeb378a9c38395f7a466cf6894” }, Node { left: “”, right: “”, parent: “a70145e430c9ffc0527e6679bd3d14c3819921c7bdcee060e3e848c1e60fe771”, sibling: “fcb0de60e5febc4dea96c4dc0153362d289f1aa5bd0797db2dcd7f23708205bb”, position: Left, data: “2ce109e9d0faf820b2434e166297934e6177b65ab9951dbc3e204cad4689b39c”, hash: “08c3a23afb58cefa0357697210f5a6be5234b45e8161963f75a58a9ba6b5a9e4” }, Node { left: “”, right: “”, parent: “a70145e430c9ffc0527e6679bd3d14c3819921c7bdcee060e3e848c1e60fe771”, sibling: “2ce109e9d0faf820b2434e166297934e6177b65ab9951dbc3e204cad4689b39c”, position: Right, data: “fcb0de60e5febc4dea96c4dc0153362d289f1aa5bd0797db2dcd7f23708205bb”, hash: “7674e6963d9a3f51967dff713d69254ad3acb05d803238bc6b725fcb10f43e8a” }, Node { left: “”, right: “”, parent: “1b67b42ffe8865f9f551071cb412895c96f5b9f52586e904d9933f57c05981b1”, sibling: “d5fe46b1204cb7bb339b929b1777e04d2c7235915d7a50d6baa134e20dbc80c6”, position: Left, data: “b0f69b8d46703ad68753ec87d319678be392a9e2fa024449750821f886e5d1af”, hash: “3bfa169f6fa2da82a6cc3f5ecdc26d56b3d4897938df6fdac431226fcd6999d5” }, Node { left: “”, right: “”, parent: “1b67b42ffe8865f9f551071cb412895c96f5b9f52586e904d9933f57c05981b1”, sibling: “b0f69b8d46703ad68753ec87d319678be392a9e2fa024449750821f886e5d1af”, position: Right, data: “d5fe46b1204cb7bb339b929b1777e04d2c7235915d7a50d6baa134e20dbc80c6”, hash: “955b23e75d9bc447bb70907b3c97f04137c539c550f69ac79ccb67f61d8dc94f” }, Node { left: “”, right: “”, parent: “71dae017bc564544250be0bcad9740c09600c9638f6962b8ef167d1d22a21e46”, sibling: “1b67b42ffe8865f9f551071cb412895c96f5b9f52586e904d9933f57c05981b1”, position: Left, data: “a70145e430c9ffc0527e6679bd3d14c3819921c7bdcee060e3e848c1e60fe771”, hash: “f50d2b271f9a6f44901225646baf708d887b9114a6d3f0aea9654f48fd608220” }, Node { left: “”, right: “”, parent: “71dae017bc564544250be0bcad9740c09600c9638f6962b8ef167d1d22a21e46”, sibling: “a70145e430c9ffc0527e6679bd3d14c3819921c7bdcee060e3e848c1e60fe771”, position: Right, data: “1b67b42ffe8865f9f551071cb412895c96f5b9f52586e904d9933f57c05981b1”, hash: “3acf37e5cbffb91875022edd43e62b46ba3c950a856938ae1cd19f92e9fb88b8” }, Node { left: “a70145e430c9ffc0527e6679bd3d14c3819921c7bdcee060e3e848c1e60fe771”, right: “1b67b42ffe8865f9f551071cb412895c96f5b9f52586e904d9933f57c05981b1”, parent: “”, sibling: “”, position: None, data: “71dae017bc564544250be0bcad9740c09600c9638f6962b8ef167d1d22a21e46”, hash: “ad52237a2e4eb1512b8bd1ccf1695ef95d9831ad7aeff0ba6718aa549991e020” }]
71dae017bc564544250be0bcad9740c09600c9638f6962b8ef167d1d22a21e46

これで合ってるか?

【Rust】string型から指定した桁数の小数点に切り抜く

Stringからf32に変換するには、stdのparseを使用する
https://doc.rust-lang.org/std/primitive.str.html#method.parse

    println!("{}", btc_price);
    println!("{}", btc_price.parse::<f32>().unwrap());

Finished `dev` profile [unoptimized + debuginfo] target(s) in 2.32s
Running `target/debug/wallet`
96058.4713474727569803
96058.47

良いね^^

f32の桁数を合わせたいときは

   println!("{}", btc_price);
    println!("{}", (btc_price.parse::<f32>().unwrap() * 1000.0).round() / 1000.0);
    println!("{}", (eth_price.parse::<f32>().unwrap() * 1000.0).round() / 1000.0);
    println!("{}", (sol_price.parse::<f32>().unwrap() * 1000.0).round() / 1000.0);
    println!("{}", (ada_price.parse::<f32>().unwrap() * 1000.0).round() / 1000.0);
    println!("{}", (avax_price.parse::<f32>().unwrap() * 1000.0).round() / 1000.0);

96176.1705240345753092
96176.17
2719.895
179.643
0.8
24.796

### Teraテンプレート側で制御する場合
round
https://keats.github.io/tera/docs/#round

<h5 class="card-market">{{eth_price | round(method="ceil", precision=2) }} usd</h5>

2714.98

サーバ側ではなく、teraでroundを使うのが良さそう

【Rust】#[warn(non_snake_case)]のwaningを消したい

#[allow(non_snake_case)] を追加する

#[derive(Debug, Serialize, Clone, Deserialize)]
#[allow(non_snake_case)]
struct Rate {
    id: String,
    symbol: String,
    // currencySymbol: String,
    r#type: String,
    rateUsd: String,
}

#[tokio::main]
async fn main() {
    let mut btc_price = "".to_string();
    let mut eth_price = "".to_string();
    let mut sol_price = "".to_string();
    let mut ada_price = "".to_string();
    let mut avax_price = "".to_string();


    let coins = get_price().await.unwrap();
    let objs: Vec<Rate> = serde_json::from_value(coins).unwrap();
    for obj in objs {
        match obj.symbol {
            val if val == "BTC".to_string() => btc_price = obj.rateUsd,
            val if val == "ETH".to_string() => eth_price = obj.rateUsd,
            val if val == "SOL".to_string() => sol_price = obj.rateUsd,
            val if val == "ADA".to_string() => ada_price = obj.rateUsd,
            val if val == "AVAX".to_string() => avax_price = obj.rateUsd,
            _ => (),
        }
    }
    println!("{},{},{},{},{}", btc_price,eth_price,sol_price,ada_price,avax_price);
}

なるほどー

【Rust】ファイルの拡張子を取得して、任意のファイル名に変更

正規表現(regex)もあるけど、単純にreplaceを使う。

let tmp_path = "./tmp/test.png";
    let str = &tmp_path.replace("./tmp/", "");
    let pos = str.find('.').unwrap();
    let file_extension = &str[(pos+1)..].to_string();
    let time = Utc::now().format("%Y%m%d%H%M%S").to_string();
    let file_key = format!("{}.{}", time, file_extension);
    println!("{}", file_key);

Finished `dev` profile [unoptimized + debuginfo] target(s) in 7.22s
Running `target/debug/axum`
20250217071013.png