【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

【Rust】base64エンコード/デコード(2回目)

これ、何回やっても忘れてしまうな…

base64= “0.22.1”

use base64::prelude::*;

fn main() {
    let ip = "192.168.33.10".to_string();
    println!("{}", base64_str(&ip));
    let str = "MTkyLjE2OC4zMy4xMA==".to_string();
    println!("{}", base64_decode(&str));
}

fn base64_str(str: &String) -> String {
    BASE64_STANDARD.encode(&str)
}

fn base64_decode_bytes(b64str: &str) -> Vec<u8> {
    let t = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    let mut table: [u8; 256] = [0; 256]; 
    for (i, v) in t.as_bytes().iter().enumerate() {
        table[*v as usize] = i as u8; 
    }
    let b64 = String::from(b64str).replace("\r", "").replace("\n", "");
    let b64bytes = b64.as_bytes();
    let mut result: Vec<u8> = vec![];
    let cnt = b64bytes.len() / 4;
    for i in 0..cnt {
        let i0 = b64bytes[i*4+0];
        let i1 = b64bytes[i*4+1];
        let i2 = b64bytes[i*4+2];
        let i3 = b64bytes[i*4+3];
        let c0 = table[i0 as usize] as usize;
        let c1 = table[i1 as usize] as usize;
        let c2 = table[i2 as usize] as usize;
        let c3 = table[i3 as usize] as usize;
        let b24 = (c0 << 18) | (c1 << 12) | (c2 <<  6) | (c3 <<  0);
        let b0 = ((b24 >> 16) & 0xFF) as u8;
        let b1 = ((b24 >>  8) & 0xFF) as u8;
        let b2 = ((b24 >>  0) & 0xFF) as u8;
        result.push(b0);
        if i2 as char != '=' { result.push(b1); }
        if i3 as char != '=' { result.push(b2); }
    }
    result
}

fn base64_decode(b64str: &str) -> String {
    String::from_utf8(base64_decode_bytes(b64str)).unwrap()
}

Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.51s
Running `target/debug/app`
MTkyLjE2OC4zMy4xMA==
192.168.33.10

【Rust】Subモジュールで定義したMutexの使い方

mutexの場合でも、基本的にはstaticと同じようにモジュール名から呼び出せば使えるようになる。

sub file

use once_cell::sync::Lazy;
use std::sync::Mutex;

pub static BOX: Lazy<Mutex<Vec<String>>> = Lazy::new(|| Mutex::new(vec![]));

main

mod sub1;

fn main() {
    sub1::BOX.lock().unwrap().push("text1".to_string());
    println!("{:?}", sub1::BOX);
}

Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.67s
Running `target/debug/app`
Lazy { cell: OnceCell(Mutex { data: [“text1”], poisoned: false, .. }), init: “..” }

なるほど、そういうことか…

【Rust】IPv4のポートを除いたアドレスをスライスする

IPv4: 10進数表記で 「3桁:3桁:3桁:3桁」
*3桁の数字は、0 〜 255 の範囲
例:192.168.1.10

fn main() {
    let str = "192.168.33.10:53218".to_string();
    let pos = str.find(':').unwrap();
    println!("{}", &str[..pos]);

}

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

“:”があるかないかを判定してから、スライスをした方が良さそう。 ただ、これ、IPv4だけなんだよな… IPv6だったらどうしよう…

pub fn slice_ipv4(str: &String) -> String {
    if str.find(':').is_some() {
        let pos = str.find(':').unwrap();
        return (&str[..pos]).to_string()
    } 
    return str.to_string()
}

【Rust】構造体にDateTime型を使用する

構造体に時間を入れて、取り出す際には”2025-02-15 05:49:42″など秒までで使いたい。
RustでDatetimeを使用すると、2025-02-15T05:49:42.648829007Zというような値になる。
datetimeをString型に変えて[0..19]で切り抜いて使おうかと思ったが、、、、formatを使えば、自由に取り出せることができる。

#[derive(Debug)]
struct TimeS {
    time: DateTime<Utc>,
}

fn main() {
    let utc_datetime: DateTime<Utc> = Utc::now();
    let t: String = utc_datetime.to_string();
    println!("{}", &t[0..19]);

    let t = TimeS { time: utc_datetime};
    println!("{:?}", t);
    println!("{}", t.time.format("%Y年%m月%d日 %H時%M分%S秒 %Z"));
}

Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.55s
Running `target/debug/app`
2025-02-15 05:49:42
TimeS { time: 2025-02-15T05:49:42.648829007Z }
2025年02月15日 05時49分42秒 UTC

こう考えると、timeはString型ではなくDateTimeとした方が良いですね。。

【Rust】Log出力を実装する

log = “0.4.25”
env_logger = “0.11.6”

use env_logger;
use log::{error, warn, info, debug};
use std::env;

fn main() {
    env::set_var("RUST_LOG", "info");
    env_logger::init();
    
    debug!("debugです");
    info!("infoです");
    warn!("warnです");
    error!("errorです");
}

[2025-02-14T07:29:59Z INFO app] infoです
[2025-02-14T07:29:59Z WARN app] warnです
[2025-02-14T07:29:59Z ERROR app] errorです

上記でも良いんだけど、log、env_logger使わずに、普通にchronoとFileだけで良い気がする。

use std::path::Path;
use chrono::{Utc, DateTime};
use std::fs::File;
use std::fs::OpenOptions;
use std::io::Write;

fn main() {
    log_output("this is test".to_string());
}

fn log_output(message: String) {
    let utc_datetime: DateTime<Utc> = Utc::now();
    let t: String = utc_datetime.to_string();
    let filename = format!("./log/{}.txt",&t[0..7]);

    let log_message = format!("[{}] {}", &t[0..19], message);
    println!("{}", log_message);

    if !Path::new(&filename).is_file() {
        let _ = File::create(&filename);
    }
    let mut file_ref = OpenOptions::new()
                        .append(true)
                        .open(filename)
                        .expect("Unable to open file");

    file_ref.write_all(&log_message.as_bytes()).expect("write failed");
    file_ref.write_all(b"\n").expect("write failed");
}

Compiling app v0.1.0 (/home/vagrant/dev/rust/app)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.54s
Running `target/debug/app`
[2025-02-14 07:58:05] this is test

【Rust】モジュール間で別のモジュールの関数を利用する

mainで利用するモジュールを定義し、使用する方のモジュールで、 use crate::${modName} と定義する。
main.rsで両方のモジュールを定義していないとエラーになるので注意が必要

main.rs

mod account;
mod crypt;

account.rs

use crate::crypt;
//
println!("{}", crypt::password_verify(&loginform.password, &password_hash));

crypt.rs

use pwhash::bcrypt;

pub fn password_verify(password: &String, hash: &String) -> bool {
    bcrypt::verify(password, hash)
}

なるほどなるほど!