Rustでmalloc, free

use std::alloc::{alloc, dealloc, Layout};
use std::ptr;

fn main() {
    unsafe {
        let layout = Layout::new::<i32>();
        let ptr = alloc(layout) as *mut i32;

        if ptr.is_null() {
            panic!("メモリ確保に失敗しました");
        }

        *ptr = 42;
        println!("ptrが指す値: {}", *ptr);

        dealloc(ptr as *mut u8, layout);
    }
}

Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.83s
Running `target/debug/memory`
ptrが指す値: 42

【Rust】getblockのCLIコマンド機能を作る

CLIでgetblockと引数にblock heightのi32を受け取って、ブロック情報を表示します。

#[derive(Subcommand)]
enum Command {
  Getnewaddress,
  Getinfo,
  Getbalance(BalanceArgs),
  Getaccount,
  Getblock(BlockArgs),
}

fn main() {
   let args = App::parse();
   match args.command {
      Command::Getnewaddress => getnewaddress(),
      Command::Getinfo => getinfo(),
      Command::Getbalance(args) => args.run(),
      Command::Getaccount => getaccount(),
      Command::Getblock(args) => args.run(),
   }
}

#[derive(Args)]
struct BlockArgs {
  height: i32,
}

impl BlockArgs {
  fn run(&self) {
    let _ = cli::get_block(self.height);
  }
}

$ ./target/debug/ruscal getblock 8
Block { height: 8, version: “0.1.0”, time: 2025-02-25T06:48:54.561868021Z, transactions: [SignedTransaction { version: “0.1.0”, time: 2025-02-25T06:47:49.809920514Z, sender: “0410f87429e89498e928d00b6a6186fdc9ccbde2ca55d5746f0e1bf8f1a1fbdb7634de5c1d5b4e49dc29bb78613fefb199d93eb8eb73545791a245c1f1ca6d5ce0”, receiver: “1FsHydJvr3nKj3KcwFhSozBf2WBKMf9jeo”, amount: 50, nft_data: “”, nft_origin: “”, op_code: “”, signature: “8A5216CD62C309A4923E9088CA9284AFC7C0261D89754FCE84735EC34A193A089B7A4C62FC32F91AE0B83549D575CE3D5EBAC1C4B9A61F0EF67D1B851F3F3E53” }, SignedTransaction { version: “0.1.0”, time: 2025-02-25T06:48:45.817409745Z, sender: “0410f87429e89498e928d00b6a6186fdc9ccbde2ca55d5746f0e1bf8f1a1fbdb7634de5c1d5b4e49dc29bb78613fefb199d93eb8eb73545791a245c1f1ca6d5ce0”, receiver: “1FsHydJvr3nKj3KcwFhSozBf2WBKMf9jeo”, amount: 60, nft_data: “”, nft_origin: “”, op_code: “”, signature: “6E73E636DDA5A5F360280442BEA308CD3C38C7A4C769BC8D4965B5106F9F6647D15B0382DE90ECFB84AE462BB81F26099DB1F8A9F0276541C44C60376FF31D16” }], hash: “0000ddb477024b0fb97c02e34bfad38eaee9891524759ba8801e06283b12cb9c”, nonce: “270778”, merkle: “713f5eb6109b03eb22cf8236171040f6bbf6171fbb2513b782b8b9fa9f41807f”, miner: “MTYwLjE2LjExNy44MA==”, validator: “MTYwLjE2LjExNy44MA==”, op: “” }

同じような容量で、gettransactionも同様に作ります。
$ ./target/debug/ruscal gettransaction 8A5216CD62C309A4923E9088CA9284AFC7C0261D89754FCE84735EC34A193A089B7A4C62FC32F91AE0B83549D575CE3D5EBAC1C4B9A61F0EF67D1B851F3F3E53

transactionの送信の場合

#[derive(Args)]
struct MoveArgs {
  pub_key: String,
  address: String,
  amount: i32
}

impl MoveArgs {
  fn run(&self) {
    // transaction送信関数を呼び出す
  }
}

$ ./target/debug/ruscal move de2ca55d5746f0e1bf8f1a1fbdb7634de5c1d5b4e49dc29bb78613fefb199d93eb8eb73545791a245c1f1ca6d5ce0 12sSVCmfi7kgsdG4ZmaargPppDRvkE5zvD 1000

なるほど、大分わかってきました。

【Rust】getnewaddressのAPI機能を作る

### コマンドライン引数の書き方

fn main() {

   let command_name = std::env::args().nth(0).unwrap_or("CLI".to_string());
   let name = std::env::args().nth(1).unwrap_or("World".to_string());

   println!("Hello {} via {}!", name, command_name);
}

$ cargo run
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.04s
Running `target/debug/ruscal`
Hello World via target/debug/ruscal!

### 上記を応用する

fn main() {
   let args = App::parse();
   match args.command {
    Command::Getnewaddress => getnewaddress(),
   }
}

fn getnewaddress() {
  address::get_address();
}
use p256::{
    ecdsa::{SigningKey},
    pkcs8::EncodePrivateKey, PublicKey,SecretKey};
use rand_core::{OsRng};
use std::{fs::File, io::Write, path::Path};
use ripemd::{Ripemd160};
use sha2::{Digest, Sha256};

pub fn get_address() {
    init_secret();
    let contents = std::fs::read_to_string("./config/tmp.pem")
        .expect("something went wrong reading the file");
    let secret_pem = contents.trim();

    let secret_key = secret_pem.parse::<SecretKey>().unwrap();
    let _private_key_serialized = hex::encode(&secret_key.to_bytes());

    let public_key = secret_key.public_key();
    let _public_key_serialized = hex::encode(&public_key.to_sec1_bytes());
    let address = create_address(&public_key);  
    println!("{}", address);
}

pub fn init_secret() {
    let path = Path::new("./config/tmp.pem");
    if path.is_file() {
        // println!("The private key, public key, and address have already been created.");
    } else {
        let secret_key = SigningKey::random(&mut OsRng);
        let secret_key_serialized = secret_key
            .to_pkcs8_pem(Default::default())
            .unwrap()
            .to_string();
        let mut file = File::create("./config/tmp.pem").expect("file not found.");
        writeln!(file, "{}", secret_key_serialized).expect("can not write.");
        println!("created a private key.");
    }
}

pub fn create_address(public_key: &PublicKey) -> String {

    let vk = public_key.to_sec1_bytes();
    let mut hasher = Sha256::new();
    hasher.update(vk);
    let hashed_sha256 = hasher.finalize();

    let mut hasher = Ripemd160::new();
    hasher.update(hashed_sha256);
    let account_id = hasher.finalize();

    let mut payload = account_id.to_vec();
    payload.insert(0, 0x00);

    let mut hasher = Sha256::new();
    hasher.update(&payload);
    let hash = hasher.finalize();

    let mut hasher = Sha256::new();
    hasher.update(hash);
    let checksum = hasher.finalize();

    payload.append(&mut checksum[0..4].to_vec());

    const ALPHABET: &str = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
    let address = base_x::encode(ALPHABET, &payload);

    return address;
}

$ cargo run getnewaddress
Compiling ruscal v0.1.0 (/home/vagrant/dev/rust/api)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.92s
Running `target/debug/ruscal getnewaddress`
1BnpTXdSbSgMZwWYq7cdU3LPSw1g4YhyHZ

【Rust】スタックマシンで関数を実装する

変数と同じように、関数名とinstructionをhashmapに保存する

/x 2 def /double { x 2 * } fn

        "fn" => {
            let tmp = func(&mut stack, instructions.clone());
            for (key, value) in &tmp {
                funcs.insert(key.to_string(), value.to_vec());
            }
            println!("{:?}", funcs); 
        } 

// 省略

fn func(stack: &mut Vec<String>, instructions: Vec<String>) -> HashMap<String, Vec<String>> {
    let mut funcs: HashMap<String, Vec<String>> = HashMap::new();
    let name = stack.pop().unwrap();

    funcs.insert(name, instructions);
    funcs
}

{“double”: [“x”, “2”, “*”]}

関数名と式をhashmapに保存したので、後は関数名が書かれていた際に、関数の内容を呼び出すだけ


    } else if funcs.contains_key(word) {
      let _ = op_fn(&mut stack, funcs.get(word).unwrap(), vars.clone());
//

fn op_fn(stack: &mut Vec<String>, input: &Vec<String>, vars: HashMap<String, String>) {
    let mut words = input.clone();
    while let Some((&ref word, rest)) = words.split_first() {
        if word.is_empty() {
          break;
        }
        if let Ok(parsed) = word.parse::<i32>() {
          stack.push(parsed.to_string());
        } else if word.starts_with("/") {
          stack.push((word[1..]).to_string());
        } else if vars.contains_key(word) {
          stack.push(vars.get(word).unwrap().to_string());
        } else {
          match word.as_str() {
            "+" => add(stack),
            "-" => sub(stack),
            "*" => mul(stack),
            "/" => div(stack),
            "%" => r#mod(stack),
            "<" => lt(stack),
            ">" => rt(stack),
            "==" => equal(stack),
            "!=" => nequal(stack),
            "sqrt" => r#sqrt(stack),
            "if" => op_if(stack),
            _ => todo!(),
            }
        }
        words = rest.to_vec();
      }
}

/x 2 def /double { x 2 * } fn double
=> [“4”]

【Rust】スタックマシンでループ文を実装するための準備

ループで実行する処理文を記憶するために、”{ }”で囲まれている中身を取得してベクターで保存しておく。

fn main() {
    let mut stack = vec![];
    let mut instructions = vec![];

    let str = "1 2 + { 10 20 + }".to_string();
    let mut words: Vec<_> = str.split(" ").collect();

    while let Some((&word, mut rest)) = words.split_first() {
        if word.is_empty() {
            break;
        }
        if word == "{" {
            (instructions, rest) = parse_instruction(rest);
        } else if let Ok(parsed) = word.parse::<i32>() {
            stack.push(parsed.to_string());
        } else {
            match word {
              "+" => add(&mut stack),
              _ => panic!("{word:?} could not be parsed"),
            }
        }
        words = rest.to_vec();
    }
    println!("{:?}", stack);
    println!("{:?}", instructions);
}

fn parse_instruction<'src, 'a>(input: &'a [&'src str])-> (Vec<String>, &'a [&'src str]) {
    let mut tokens: Vec<String> = vec![];
    let mut words = input;

    while let Some((&word, mut rest)) = words.split_first() {
        if word.is_empty() {
            break;
        }
        if word == "}" {
            return (tokens, rest);
        } else {
            tokens.push(word.to_string())
        }
        words = rest;
    }
    (tokens, words)
}

fn add(stack: &mut Vec<String>) {
    let rhs: i32 = stack.pop().unwrap().parse::<i32>().unwrap();
    let lhs: i32 = stack.pop().unwrap().parse::<i32>().unwrap();
    stack.push((lhs + rhs).to_string());
}

Running `target/debug/sample`
[“3”]
[“10”, “20”, “+”]

【Rust】sqrtの計算

f64でsqrt()の関数があるので、f64に変換して計算する
https://doc.rust-lang.org/std/primitive.f64.html#method.sqrt

fn main() {
    let n = 10;
    let f = n as f64;
    let s = f.sqrt();
    println!("{}", s);
    println!("{}", s as i32);
}

これをスタックマシンに実装します。とりあえずi32で統一しているので、ここでは、sqrt後 as i32としています。

match word {
        "+" => add(&mut stack),
        "-" => sub(&mut stack),
        "*" => mul(&mut stack),
        "/" => div(&mut stack),
        "%" => r#mod(&mut stack),
        "<" => lt(&mut stack),
        ">" => rt(&mut stack),
        "sqrt" => r#sqrt(&mut stack),
        "if" => op_if(&mut stack),
        "def" => {  
          let tmp = op_def(&mut stack);
          for (key, value) in &tmp {
            vars.insert(key.to_string(), value.to_string());
          }
        },
        _ => panic!("{word:?} could not be parsed"),
      }
//

fn r#sqrt(stack: &mut Vec<String>) {
  let f: f64 = stack.pop().unwrap().parse::<f64>().unwrap();
  let s = f.sqrt();
  stack.push((s as i32).to_string());
}

【Rust】四則演算に括弧{} をつける

fn main() {

  let mut stack = vec![];

  // Reverse Polish Notation
  let str = "100 5 - { 1 2 + } +".to_string();

  let mut words: Vec<_> = str.split(" ").collect();
  while let Some((&word, mut rest)) = words.split_first() {
    if word.is_empty() {
      break;
    }
    if word == "{" {
      let value;
      (value, rest) = parse_block(rest);
      stack.push(value);
    } else if let Ok(parsed) = word.parse::<i32>() {
      stack.push(parsed);
    } else {
      match word {
        "+" => add(&mut stack),
        "-" => sub(&mut stack),
        "*" => mul(&mut stack),
        "/" => div(&mut stack),
        _ => panic!("{word:?} could not be parsed"),
      }
    }
    words = rest.to_vec();
  }
  println!("{:?}", stack); 
}

fn parse_block<'src, 'a>(input: &'a [&'src str]) -> (i32, &'a [&'src str]){
  let mut tokens: Vec<i32> = vec![];
  let mut words = input;

  while let Some((&word, mut rest)) = words.split_first() {
    if word.is_empty() {
      break;
    }
    if word == "}" {
      return (tokens[0], rest);

    } else if let Ok(parsed) = word.parse::<i32>() {
      tokens.push(parsed);
    } else {
      match word {
        "+" => add(&mut tokens),
        "-" => sub(&mut tokens),
        "*" => mul(&mut tokens),
        "/" => div(&mut tokens),
        _ => todo!(),
        // _ => panic!("{word:?} could not be parsed"),
        }
    }
    words = rest;
  }
  (tokens[0], words)
}

fn add(stack: &mut Vec<i32>) {
  let rhs = stack.pop().unwrap();
  let lhs = stack.pop().unwrap();
  stack.push(lhs + rhs);
}

fn sub(stack: &mut Vec<i32>) {
  let rhs = stack.pop().unwrap();
  let lhs = stack.pop().unwrap();
  stack.push(lhs - rhs);
}

fn mul(stack: &mut Vec<i32>) {
  let rhs = stack.pop().unwrap();
  let lhs = stack.pop().unwrap();
  stack.push(lhs * rhs);
}

fn div(stack: &mut Vec<i32>) {
  let rhs = stack.pop().unwrap();
  let lhs = stack.pop().unwrap();
  stack.push(lhs / rhs);
}

[98]

【Rust】&[&str]の加算

&[&str]と&[&str]の加算の方法が不明だったので、一旦Stringに変えてformat!で加算する

fn main() {
    let str = "100 5 - { 1 2 + }".to_string();
    let mut words: Vec<_> = str.split(" ").collect();
    let Some((&word, mut rest)) = words.split_first() else { todo!() };

    println!("{:?}", rest);

    let mut tokens: Vec<_> = vec![];
    tokens.push(1);
    let mut str = "init".to_string();
    str = format!("{} {}", str, tokens[0].to_string());
    for r in rest {
        str = format!("{} {}", str, r);
        println!("{}", str);
    }
    println!("{:?}", str);   
}

“init 1 5 – { 1 2 + }”

うーん、なんか違う気がする..

【Rust】HashMapのValueへのアクセス

employee[“A”].clone().pop() ではなく、要素を指定せずにvalueをpop()したい場合

use std::collections::HashMap;

fn main() {
    let mut employee: HashMap<String, Vec<String>> = HashMap::new();
    employee.insert("A".to_string(), vec!["X".to_string(), "Y".to_string(), "Z".to_string()]);
    employee.insert("B".to_string(), vec!["X".to_string(), "Y".to_string(), "Z".to_string()]);
    for (key, mut value) in employee {
        println!("{:?}", value.pop());
    }
}

for e in employee ってしてしまうと、eからvalueの取り出し方法がわからなくなる。
これに結構時間かかりました。

vectorの要素のindexを取得する方法

        println!("{:?}", value.iter().position(|n| n == "Y"));
use std::collections::HashMap;

fn main() {

   let mut employee: HashMap<String, Vec<String>> = HashMap::new();
   employee.insert("A".to_string(), vec!["X".to_string(), "Y".to_string(), "Z".to_string()]);
   employee.insert("B".to_string(), vec!["X".to_string(), "Y".to_string(), "Z".to_string()]);
   employee.insert("C".to_string(), vec!["Y".to_string(), "Z".to_string(), "X".to_string()]);

   let mut department: HashMap<String, Vec<String>> = HashMap::new();
   department.insert("X".to_string(), vec!["C".to_string(), "A".to_string(), "B".to_string()]);
   department.insert("Y".to_string(), vec!["B".to_string(), "C".to_string(), "A".to_string()]);
   department.insert("Z".to_string(), vec!["A".to_string(), "B".to_string(), "C".to_string()]);

   let res = stable_match(employee, department);
   println!("{:?}", res);
}

fn stable_match(request_dict: HashMap<String, Vec<String>>, accept_dict: HashMap<String, Vec<String>>) -> HashMap<String, String>{
   let mut match_dict: HashMap<String, String> = HashMap::new();
   let req_len = request_dict.len();

   while match_dict.len() < req_len {
      'label: for (req_name, req_value) in &request_dict {
         for value in match_dict.values() {
            if value == req_name {
               break 'label;
            }
         }
         let acc_name = req_value.pop();
         if !match_dict.contains_key(&acc_name) {
            match_dict.insert(acc_name.unwrap(), req_name.to_string());
         } else {
            let rival_name = match_dict[&acc_name];
            if accept_dict[&acc_name].iter().position(|n| n == req_name) < accept_dict[&acc_name].iter().position(|n| n == rival_name) {
               match_dict.insert(acc_name.unwrap(), req_name.to_string());
            }
         }
      }
   }
   return match_dict;
}

うーむ、上手く書けん…

【Rust】ラムダ関数とは

– ラムダ計算とは関数型言語の基盤となっている計算モデルで、数値や演算の具体的な表記ではなく、「関数そのもの」を使って計算を表現する
– Rustにおけるクロージャは、その外側の環境を捕捉した関数のこと
– クロージャの構文や機能は、その場限りの用途で何かを作るのに便利

   let sq = |n| n * n;
   println!("{}", sq(9));

81

   let i = 32;
   let v = |n| n * i;
   println!("{}", v(9));

288

なるほどね