【Rust】txtファイルを読み込む

use serde::{Serialize, Deserialize};
use std::fs::File;
use std::io::prelude::*;

fn main() -> std::io::Result<()>{
    let mut file = File::open("foo.txt")?;
    let mut contents = String::new();
    file.read_to_string(&mut contents)?;
    println!("{}", contents);
    assert_eq!(contents, "hello, world!");
    Ok(())
}

$ cargo run
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.49s
Running `target/debug/sample`
bytes bytes bytes 8bytes
asdfghjkl
1234567890
thread ‘main’ panicked at src/main.rs:11:5:
assertion `left == right` failed
left: “bytes bytes bytes 8bytes\nasdfghjkl\n1234567890”
right: “hello, world!”
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

これも同じ

fn main() -> std::io::Result<()>{
    let file = File::open("foo.txt")?;
    let mut buf_reader = BufReader::new(file);
    let mut contents = String::new();
    buf_reader.read_to_string(&mut contents)?;
    println!("{}", contents);
    assert_eq!(contents, "hello, world!");
    Ok(())
}

### 1行ずつ読み取る

use std::fs::File;
use std::io::{BufReader, BufRead, Error};

fn main() -> Result<(), Error>{
    let path = "foo.txt";
    let input = File::open(path)?;
    let buffered = BufReader::new(input);

    for line in buffered.lines() {
        println!("{}", line?);
    }

    Ok(())
}

### ファイルの書き込み

fn main() -> std::io::Result<()>{
    let mut buffer = File::create("hoge.txt")?;

    buffer.write_all(b"some bytes\n hogehoge")?;
    Ok(())
}

### 最終行に書き込み

use std::fs::OpenOptions;
use std::io::Write;

fn main() -> std::io::Result<()>{
    let mut fileRef = OpenOptions::new()
                        .append(true)
                        .open("hoge.txt")
                        .expect("Unable to open file");

    fileRef.write_all(b"last line\n").expect("write failed");
    Ok(())
}

改行コードを入れないと、同じ列になってしまう。
さて、Structでやりたい。

【Rust】jsonファイルの書き換え

jsonデータを読み込んで、書き換え

use serde::{Serialize, Deserialize};
use std::fs;
use std::fs::File;
use std::io::prelude::*;

#[derive(Serialize, Deserialize, Debug)]
#[allow(non_snake_case)]
struct Rotator {
    Pitch:f32,
    Roll:f32,
    Yaw:f32,
}

#[derive(Serialize, Deserialize, Debug)]
#[allow(non_snake_case)]
struct CharaParam {
    Name: String,
    Id: i32,
    ItemIdList: Vec<u32>,
    RotData: Rotator,
}

fn main(){

    let input_fn = fs::read_to_string("test_input.json")
        .expect("JSON Read Failed.");
    
    let mut param_list: Vec<CharaParam> = serde_json::from_str(&input_fn).unwrap();

    let chara00 = CharaParam{Name:String::from("Apple"), Id:0x01, ItemIdList:vec![1000, 1001], RotData:Rotator{Pitch:0.0, Roll:0.0, Yaw:32.0} };
    let chara01 = CharaParam{Name:String::from("Banana"), Id:0x02, ItemIdList:vec![1002, 1003], RotData:Rotator{Pitch:0.0, Roll:-70.0, Yaw:66.0}};

    param_list.push(chara00);
    param_list.push(chara01);

    let output_fn = String::from("test_output.json");
    let result = output_json(&output_fn, param_list);
    match result {
        Ok(..) => { println!("Json output finished.【{}】", output_fn) }
        Err(err) => { println!("Error: {}", err)}
    }

}

fn output_json(output_fn: &str, charaparam_list: Vec<CharaParam>) -> std::io::Result<()>{

    let serialized: String = serde_json::to_string(&charaparam_list).unwrap();

    let mut file = File::create(output_fn)?;
    file.write_all(serialized.as_bytes())?;
    Ok(())
}

[{“Name”:”Kiwi”,”Id”:3,”ItemIdList”:[2000,2001],”RotData”:{“Pitch”:0.0,”Roll”:0.0,”Yaw”:0.0}},{“Name”:”Orange”,”Id”:4,”ItemIdList”:[2002,2003],”RotData”:{“Pitch”:1.0,”Roll”:50.2,”Yaw”:66.5}},{“Name”:”Apple”,”Id”:1,”ItemIdList”:[1000,1001],”RotData”:{“Pitch”:0.0,”Roll”:0.0,”Yaw”:32.0}},{“Name”:”Banana”,”Id”:2,”ItemIdList”:[1002,1003],”RotData”:{“Pitch”:0.0,”Roll”:-70.0,”Yaw”:66.0}}]

書き込みは出来ていますが、処理量が増えてくると処理が冗長になるように思います。

【Rust】文字列の繰り返し

文字列の繰り返しは repeat() を使用する

fn main() {
    let mut difficulty = 4;
    let str: String = "0".to_string();
    let target = str.repeat(difficulty);
    println!("{}", target);
}

$ cargo run
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.29s
Running `target/debug/crypt`
0000

リファクタリングする。

fn main() {
    let now = time::Instant::now();
    let previous_hash = "b9b9ee9ffc95fa4956b63b6043a99d0a8f04e0e52e687fc1958d3c6dff885f01";
    let mut num = rand::thread_rng().gen_range(0..1000000);
    let mut hash_num = format!("{}{}", previous_hash, num.to_string());
    let mut header = Sha256::digest(hash_num);
    let mut target: String  = (&hex::encode(header)[..4]).to_string();
    
    let mut cnt = 1;
    println!("count: {} {:x}", cnt, header);

    let mut difficulty = 4;
    let str: String = "0".to_string();
    let difficulty_str = str.repeat(difficulty);

    while target != difficulty_str {
        println!("count: {} {:x}", cnt, header);
        num = rand::thread_rng().gen_range(0..1000000);
        hash_num = format!("{}{}", previous_hash, num.to_string());
        header = Sha256::digest(hash_num);
        target = (&hex::encode(header)[..4]).to_string();
        cnt += 1;
    }
    println!("count: {} {:x}", cnt, header);
    println!("{:?}", now.elapsed());
}

$ cargo run

count: 60678 0000b49915dc1fbb5fc8660d285674dbbe9e8d06f31fc5040b6044b4fb3da86a
2.082390487s

問題ありません。なるほどね。これで、difficultyの値を自動調整できるようになりました。

【Rust】jsonの読み書き

### Jsonの読み込み

use serde::{Serialize, Deserialize};
use std::fs;
use std::io::prelude::*;

#[derive(Serialize, Deserialize, Debug)]
#[allow(non_snake_case)]
struct Rotator {
    Pitch:f32,
    Roll:f32,
    Yaw:f32,
}

#[derive(Serialize, Deserialize, Debug)]
#[allow(non_snake_case)]
struct CharaParam {
    Name: String,
    Id: i32,
    ItemIdList: Vec<u32>,
    RotData: Rotator,
}

fn main(){
    let input_fn = fs::read_to_string("test_input.json")
        .expect("JSON Read Failed.");
    let deserialized: Vec<CharaParam> = serde_json::from_str(&input_fn).unwrap();

    for data in &deserialized {
        println!("{:?}", data);
    }
}

Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.67s
Running `target/debug/sample`
CharaParam { Name: “Kiwi”, Id: 3, ItemIdList: [2000, 2001], RotData: Rotator { Pitch: 0.0, Roll: 0.0, Yaw: 0.0 } }
CharaParam { Name: “Orange”, Id: 4, ItemIdList: [2002, 2003], RotData: Rotator { Pitch: 1.0, Roll: 50.2, Yaw: 66.5 } }

### Jsonの書き込み

use serde::{Serialize, Deserialize};
use std::fs::File;
use std::io::prelude::*;

#[derive(Serialize, Deserialize, Debug)]
#[allow(non_snake_case)]
struct Rotator {
    Pitch:f32,
    Roll:f32,
    Yaw:f32,
}

#[derive(Serialize, Deserialize, Debug)]
#[allow(non_snake_case)]
struct CharaParam {
    Name: String,
    Id: i32,
    ItemIdList: Vec<u32>,
    RotData: Rotator,
}

fn main(){
    let chara00 = CharaParam{Name:String::from("Apple"), Id:0x01, ItemIdList:vec![1000, 1001], RotData:Rotator{Pitch:0.0, Roll:0.0, Yaw:32.0} };
    let chara01 = CharaParam{Name:String::from("Banana"), Id:0x02, ItemIdList:vec![1002, 1003], RotData:Rotator{Pitch:0.0, Roll:-70.0, Yaw:66.0}};

    let mut param_list:Vec<CharaParam> = Vec::new();
    param_list.push(chara00);
    param_list.push(chara01);

    let output_fn = String::from("test_output.json");
    let result = output_json(&output_fn, param_list);
    match result {
        Ok(..) => { println!("Json output finished.【{}】", output_fn) }
        Err(err) => { println!("Error: {}", err)}
    }

}

fn output_json(output_fn: &str, charaparam_list: Vec<CharaParam>) -> std::io::Result<()>{

    let serialized: String = serde_json::to_string(&charaparam_list).unwrap();

    let mut file = File::create(output_fn)?;
    file.write_all(serialized.as_bytes())?;
    Ok(())
}

読み込み、書き込みはわかったが、書き込み時に、最終行にappendとしたい。。果たして出来るんか??
単純にjsonファイルを読み取って、vectorにデータを追加してjsonファイルに保存で良い?

【Rust】構造体(struct)の要素にvector

#[derive(Debug, Serialize, Clone, Deserialize)]
struct Name {
    family: String,
    first: String,
    age: u32,
    hobby: Vec<String>,
}

fn main(){
    let n1 = Name {family: "yamada".to_string(), first: "taro".to_string(), age: 20, hobby: vec!["サッカー".to_string(), "旅行".to_string()]};

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

$ cargo run
Compiling sample v0.1.0 (/home/vagrant/dev/rust/sample)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.34s
Running `target/debug/sample`
Name { family: “yamada”, first: “taro”, age: 20, hobby: [“サッカー”, “旅行”] }

VecのところをVec<(構造体名)>でもできる。ちょっとややこしいけど…

use serde::{Serialize, Deserialize};
use once_cell::sync::Lazy;
use std::sync::Mutex;
use std::ops::DerefMut;


#[derive(Debug, Serialize, Clone, Deserialize)]
struct Name {
    family: String,
    first: String,
    age: u32,
    note: String,
}

#[derive(Debug, Serialize, Clone, Deserialize)]
struct list {
    family: String,
    first: String,
    age: u32,
    note: Vec<Name>,
}

static VECT: Lazy<Mutex<Vec<Name>>> = Lazy::new(|| Mutex::new(vec![]));

fn push_name() {
    let n1 = Name {family: "yamada".to_string(), first: "taro".to_string(), age: 20, note: "AAA".to_string()};
    VECT.lock().unwrap().push(n1);
    let n2 = Name {family: "tanaka".to_string(), first: "kazuo".to_string(), age: 18, note: "BBB".to_string()};
    VECT.lock().unwrap().push(n2);
    let n3 = Name {family: "sato".to_string(), first: "hanako".to_string(), age: 22, note: "CCC".to_string()};
    VECT.lock().unwrap().push(n3);
}

fn main(){
    push_name();

    let mut binding = VECT.lock().unwrap();
    let objs = binding.deref_mut();

    let l1 = list {family: "yamada".to_string(), first: "taro".to_string(), age: 20, note: objs.to_vec()};

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

$ cargo run
Compiling sample v0.1.0 (/home/vagrant/dev/rust/sample)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.28s
Running `target/debug/sample`
Name2 { family: “yamada”, first: “taro”, age: 20, note: [Name { family: “yamada”, first: “taro”, age: 20, note: “AAA” }, Name { family: “tanaka”, first: “kazuo”, age: 18, note: “BBB” }, Name { family: “sato”, first: “hanako”, age: 22, note: “CCC” }] }

うむ、普通に出来ますね。これをマイニング終了のタイミングでBlockとトランザクションで作りたい。

use serde::{Serialize, Deserialize};

#[derive(Debug, Serialize, Clone, Deserialize)]
struct Block {
    time: String,
    transactions: Vec<String>,
    hash: String,
    nonce: String,
}

fn main(){
    let t = vec!["sender".to_string(), "receiver".to_string(), "amount".to_string()];
    let b = Block{time:"a".to_string(), transactions: t, hash:"aaa".to_string(), nonce:"aaa".to_string()};
    println!("{:?}", b);
}

$ cargo run
Compiling sample v0.1.0 (/home/vagrant/dev/rust/sample)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.31s
Running `target/debug/sample`
Block { time: “a”, transactions: [“sender”, “receiver”, “amount”], hash: “aaa”, nonce: “aaa” }

これを作っていきます!

【Rust】axumでPOSTされたデータをreqwestで外部にPOSTする

axum::routing::postでPOSTデータを受け取って、そのデータをクローンしたものをシリアライズしてstring型にし、reqwestで外部に送る。
なお、reqwestで外部に送る処理は関数にして外出しする。

    let app = axum::Router::new()
        .route("/", axum::routing::get(handle_index))
        .route("/home", axum::routing::get(handle_top))
        .route("/account", axum::routing::get(handle_account))
        .route("/withdrawal", axum::routing::get(handle_withdrawal))
        .route("/sent", axum::routing::post(handle_sent))
        .route("/post", axum::routing::post(handle_post));

//

async fn handle_sent(axum::Form(unsignedtransaction): axum::Form<UnsignedTransaction>)
    -> axum::response::Html<String> {

    let s: UnsignedTransaction = unsignedtransaction.clone();
    post(s).await;
    let tera = tera::Tera::new("templates/*").unwrap();

    let mut context = tera::Context::new();
    context.insert("time", &unsignedtransaction.time);
    context.insert("receiver", &unsignedtransaction.receiver);
    context.insert("amount", &unsignedtransaction.amount);

    let output = tera.render("sent.html", &context);
    axum::response::Html(output.unwrap())

}

async fn post(unsignedtransaction: UnsignedTransaction) -> Result<(), Box<dyn std::error::Error>> {

    let serialized: String = serde_json::to_string(&unsignedtransaction).unwrap();

    let client = reqwest::Client::new();
    let resp = client.post("http://httpbin.org/post")
        .body(serialized)
        .send()
        .await?;
    
    let body = resp.text().await?;    
    let json: serde_json::Value = serde_json::from_str(&body)?;
    let obj = json.as_object().unwrap();
    
    for (key,value) in obj.iter() {
        println!("{}\t{}",key,value);
        }
    Ok(())
}

$ cargo run
//
Finished `dev` profile [unoptimized + debuginfo] target(s) in 4.81s
Running `target/debug/axum_basic`
args {}
data “{\”time\”:\”2025-01-02T10:53:00.295Z\”,\”sender\”:\”12sSVCmfi7kgsdG4ZmaargPppDRvkE5zvD\”,\”receiver\”:\”12sSVCmfi7kgsdG4ZmaargPppDRvkE5zvD\”,\”amount\”:1000}”
files {}
form {}
headers {“Accept”:”*/*”,”Content-Length”:”143″,”Host”:”httpbin.org”,”X-Amzn-Trace-Id”:”Root=1-67767470-5a86f0272c93fb6a0a20a060″}
json {“amount”:1000,”receiver”:”12sSVCmfi7kgsdG4ZmaargPppDRvkE5zvD”,”sender”:”12sSVCmfi7kgsdG4ZmaargPppDRvkE5zvD”,”time”:”2025-01-02T10:53:00.295Z”}
origin “*.*.*”
url “http://httpbin.org/post”

うん、ちゃんとエラーなく送れてますね。
OK, wallet側はアドレスの作成とコインのPOST処理ができてある程度形になってきたので、一旦トランザクションからBlockを作成するところに戻ります。やっぱりUIがあると楽しいですな。。

【Rust】axumでformの送信とトランザクションのPostを実装したい

templates/withdrawal.html

<form action="/sent" method="post" class="">
            <input type="hidden" name="time" id="time" value="">
            <input type="hidden" name="sender" value="{{address}}">
            <div class="mb-3">
                <label for="receiver" class="form-label">送付先アドレス</label>
                <input type="text" class="form-control" id="receiver" name="receiver" placeholder="送付先のアドレスを入力してください">
              </div>
              <div class="mb-3">
                <label for="amount" class="form-label">送付コイン量</label>
                <input type="text" class="form-control" id="amount" name="amount" placeholder="数量を半角数字で入力してください。e.g. 1000">
              </div>
              <input type="submit" value="送信" class="btn btn-primary"/>
        </form>
#[derive(Serialize, Deserialize, Debug)]
struct UnsignedTransaction {
    time: String,
    sender: String,
    receiver: String,
    amount: i32,
}
// 
async fn handle_sent(axum::Form(unsignedtransaction): axum::Form<UnsignedTransaction>)
    -> axum::response::Html<String> {

    let tera = tera::Tera::new("templates/*").unwrap();

    let mut context = tera::Context::new();
    context.insert("time", &unsignedtransaction.time);
    context.insert("receiver", &unsignedtransaction.receiver);
    context.insert("amount", &unsignedtransaction.amount);

    let output = tera.render("sent.html", &context);
    axum::response::Html(output.unwrap())
}

formのPOSTはできました。
このデータを受け取ったタイミングで、外部のIP(node)に合わせてPOSTしたい。

【Rust】秘密鍵・公開鍵・アドレスの作成をaxumを使ってWebで表現

templates/account.html

   <body class="container">
        <h1 class="display-6 my-2">Crypt Wallet</h1>
        <hr>
        <nav aria-label="breadcrumb">
            <ol class="breadcrumb">
              <li class="breadcrumb-item"><a href="/">Home</a></li>
              <li class="breadcrumb-item"><a href="/account">アカウント情報</a></li>
            </ol>
          </nav>
        <div class="alert alert-primary">
            <p class="my-2">秘密鍵、公開鍵、アドレスを生成しました。</p>
        </div>
        <dl class="row">
            <dt class="col-sm-3">秘密鍵</dt>
            <dd class="col-sm-9">{{private_key}}</dd>
          
            <dt class="col-sm-3">公開鍵</dt>
            <dd class="col-sm-9">{{public_key}}</dd>
          
            <dt class="col-sm-3">アドレス</dt>
            <dd class="col-sm-9">{{address}}</dd>
          
          </dl>
        <br><br>
        <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-geWF76RCwLtnZ8qwWowPQNguL3RmwHVBC9FhGdlKrxdiJJigb/j/68SIy3Te4Bkz" crossorigin="anonymous"></script>
    </body>
async fn handle_account()-> axum::response::Html<String> {

    let signing_key = SigningKey::random(&mut OsRng);
    let private_key = hex::encode(signing_key.to_bytes());
    let verifying_key = signing_key.verifying_key();
    let public_key = hex::encode(verifying_key.to_sec1_bytes());
    let address = new_address(&verifying_key);    

    let tera = tera::Tera::new("templates/*").unwrap();
    let mut context = tera::Context::new();
    context.insert("private_key", &private_key);
    context.insert("public_key", &public_key);
    context.insert("address", &address);

    let output = tera.render("account.html", &context);
    axum::response::Html(output.unwrap())
}

リファクタリングが必要だけど、やりたいことは大体できている^^

【Rust】axumでget, post, psql

まずテーブル作成
$ sudo -u postgres psql
$ CREATE TABLE “todo” (
id UUID primary key,
description varchar(100) not null,
deadline_at timestamp not null
);

postgres=# \dt
List of relations
Schema | Name | Type | Owner
——–+——–+——-+———-
public | todo | table | postgres

chrono = "0.4.31"
sqlx = { version = "0.7.2", features = ["runtime-tokio-native-tls", "chrono", "uuid"]}
uuid = { version = "1.6.1", fatures = ["v4", "serde"] }
use axum::{Router, extract::State, response::Html, routing::get};
use tera::{Context, Tera};
use serde::{Serialize, Deserialize};
use sqlx::{FromRow, PgPool};
use uuid::Uuid;

//

#[derive(Clone)]
struct ServiceState {
    tera: Tera,
    pool: PgPool,
}

//

#[tokio::main]
async fn main() {

    let pool = PgPool::connect("postgres://postgres:password@localhost:5432/postgres").await.unwrap();

    let tera = match Tera::new("templates/**/*.html"){
        Ok(t) => t,
        Err(e) => {
            println!("Parsing error(s): {}", e);
            ::std::process::exit(1);
        }
    };

    
    let app = Router::new()
        .route("/", get(index))
        .with_state(ServiceState {tera, pool});
    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
    axum::serve(listener, app).await.unwrap();
}

http://192.168.33.10:3000/create_todo

### 入力結果の表示
templates/todos.html

<a href="/create_todo">Todoを作成</a>
<table>
    <thread>
        <tr>
            <th scope="col">description</th>
            <th scope="col">deadline at</th>
        </tr>
    </thread>
    <tbody>
        {% for todo in todos %}
        <tr id="{{ todo.id }}">
            <td>{{ todo.description }}</td>
            <td>{{ todo.deadline_at }}</td>
        </tr>
        {% endfor %}
    </tbody>
</table>
use axum::{Router, extract::State, response::{Redirect, Html}, Form, routing::get};
use chrono::NaiveDateTime;
use tera::{Context, Tera};
use serde::{Serialize, Deserialize};
use sqlx::{FromRow, PgPool};
use uuid::Uuid;

pub fn deserialize_date<'de, D: serde::Deserializer<'de>>(
    deserializer: D,
) -> Result<NaiveDateTime, D::Error> {
    let s = String::deserialize(deserializer)?;
    NaiveDateTime::parse_from_str(&s, "%Y-%m-%dT%H:%M")
        .map_err(serde::de::Error::custom)
}

#[derive(Serialize)]
struct Index {
    name: String
}

#[derive(Clone)]
struct ServiceState {
    tera: Tera,
    pool: PgPool,
}

#[derive(Debug, Serialize, FromRow)]
struct Todo {
    id: Uuid,
    description: String,
    deadline_at: NaiveDateTime,
}

#[derive(Debug, Deserialize)]
struct CreateTodo {
    description: String,
    #[serde(deserialize_with = "deserialize_date")]
    deadline_at: NaiveDateTime,
}

async fn index(State(state): State<ServiceState>) -> Html<String> {
    let index = Index { name: String::from("test") };
    let page = state.tera.render("index.html", &Context::from_serialize(&index).unwrap()).unwrap();
    Html(page.to_owned())
}

async fn get_create_todo(State(state): State<ServiceState>) -> Html<String> {
    let page = state.tera.render("create_todo.html", &Context::new()).unwrap();
    Html(page.to_owned())
}

async fn post_create_todo(
    State(state): State<ServiceState>,
    Form(todo): Form<CreateTodo>,
) -> Redirect {
    let todo = Todo {
        id: Uuid::new_v4(),
        description: todo.description,
        deadline_at: todo.deadline_at,
    };

    sqlx::query("INSERT INTO todo VALUES ($1, $2, $3);")
        .bind(todo.id)
        .bind(todo.description)
        .bind(todo.deadline_at)
        .execute(&state.pool)
        .await
        .expect("todoの取得に失敗しました");

    Redirect::to("/todos")
}

async fn get_todos(
    State(state): State<ServiceState>,
) -> Html<String> {
    let todos = sqlx::query_as::<_, Todo>("SELECT * FROM todo")
        .fetch_all(&state.pool)
        .await
        .expect("todoの取得に失敗しました");
    let mut context = Context::new();
    context.insert("todos", &todos);

    let page = state.tera.render("todos.html", &context).expect("todoの描画に失敗しました");
    Html(page)
}

#[tokio::main]
async fn main() {

    let pool = PgPool::connect("postgres://postgres:password@localhost:5432/postgres").await.unwrap();

    let tera = match Tera::new("templates/**/*.html"){
        Ok(t) => t,
        Err(e) => {
            println!("Parsing error(s): {}", e);
            ::std::process::exit(1);
        }
    };

    
    let app = Router::new()
        .route("/", get(index))
        .route("/todos", get(get_todos))
        .route("/create_todo", get(get_create_todo).post(post_create_todo))
        .with_state(ServiceState {tera, pool});
    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
    axum::serve(listener, app).await.unwrap();
}

http://192.168.33.10:3000/todos
Todoを作成
description deadline at
test 2025-01-01T15:54:00
aaa 2025-01-01T15:57:00

なんだこれは…

【Rust】#[derive]アトリビュートとは?

derive: 導出
#[derive]アトリビュートを用いることで、型に対して特定のトレイトの標準的な実装を提供する

比較: Eq, PartialEq, Ord, PartialOrd コンパイルの条件分岐
Clone: &TからT
Copy: to give a type copy semantic
Hash: ハッシュ値計算
Default: 空のインスタンス
Debug: {:?}

implementation

struct Circle {
    x: f64,
    y: f64,
    radius: f64,
}

impl Circle {
    fn area(&self) -> f64 {
        std::f64::consts::PI * (self.radius * self.radius)
    }
}

fn main(){
    
    let a = Circle{x: 2.0, y: 4.0, radius: 3.0};
    println!("{}", a.area());
}

トレイト

struct Circle {
    x: f64,
    y: f64,
    radius: f64,
}

trait HasArea {fn area(&self)-> f64;}
impl HasArea for Circle {
    fn area(&self) -> f64 {
        std::f64::consts::PI * (self.radius * self.radius)
    }
}

fn main(){
    
    let a = Circle{x: 2.0, y: 4.0, radius: 3.0};
    println!("{}", a.area());
}

属性アトリビュートは追加機能
以下のような目的で使用する
– クレート名、バージョン、種類(バイナリか、ライブラリか)の設定
– リントの無効化 (警告の抑止)
– コンパイラ付属の機能(マクロ、glob、インポートなど)の有効化
– 外部ライブラリへのリンク
– ユニットテスト用の関数として明示
– ベンチマーク用の関数として明示
#[test] 単体テスト
#[cfg] 条件を提示し、その条件に応じたコンパイルをする
#[derive] トレイトの実装を自動的に構造体や列挙型に実装できる
 #[derive(Debug)] の場合は、Debugトレイトのfmt関数が実装される
#[allow]
#[deny]