unwrap()は、OptionがSomeの場合はその中の値を返し、Noneの場合はパニックを引き起こす。
fn main(){ let value = Some(10); let v = value.unwrap(); println!("v: {}", v); }
ソフトウェアエンジニアの技術ブログ:Software engineer tech blog
随机应变 ABCD: Always Be Coding and … : хороший
unwrap()は、OptionがSomeの場合はその中の値を返し、Noneの場合はパニックを引き起こす。
fn main(){ let value = Some(10); let v = value.unwrap(); println!("v: {}", v); }
Rustの「unwrap」と「?」はエラーハンドリングに使われる方法
unwrapはOption型やResult型の値がSomeやOkであることを前提として値を取り出す。NoneやErrだった場合は、panic!マクロが呼ばれてプログラムがクラッシュする。
?演算子は、Okの場合は値を返し、Errの場合はそのErrを呼び出し元に返す。
なるほど、OptionとResultがわかってないと、unwrapと?もさっぱり理解できませんね。。
Result
enum Result<T, E>{ Ok(T), Err(E), }
fn get_value_bad(v: bool, result: &mut usize) -> usize { if v { *result = 100; true } else { false } } fn get_value_good(v: bool) -> Result<usize, &'static str> { if v { OK(100) } else { Err("error message") } } fn main(){ let mut result = 0; if get_value_bad(true, &mut result){ println!("success: {}", result); } else { println!("failure"); } match get_value_good(true){ Ok(result) => println!("success: {}", result), Err(msg) => println!("failure") } }
fn plus_one(x: Option<i32>) -> Option<i32> { match x { None => None, Some(i) => Some(i + 1), } } fn main(){ let five = Some(5); let six = plus_one(five); let none = plus_one(None); }
use std::fs::File; fn main(){ let f = File::open("hello.txt"); let _f = match f { Ok(file) => file, Err(error) => { panic!("There was a problem opening the file: {:?}", error); } }; }
OptionはSome, None, ResultはOk, Errで共にエラーハンドリングなどで使うのね。
無効な値を取ることができる便利な列挙型としてOption型がある。
enum Option<T>{ Some(T), None, }
まず列挙型について
C++の列挙型
#include <stdio.h> enum week { Mon, Tue, Wed, Thu, Fri, Sat, Sun, }; int main(void) { enum week wk0, wk1, wk2; wk0 = Mon; wk1 = Tue; wk2 = Thu; printf("Monの値は:%d\n", wk0); printf("Tueの値は:%d\n", wk1); printf("Thuの値は:%d\n", wk2); }
#[derive(Debug)] enum Week { Mon, Tue, Wed, Thu, Fri, Sat, Sun } fn main(){ let wk0 = Week::Mon; let wk1 = Week::Tue; let wk2 = Week::Thu; println!("Monの値は{:?}", wk0); println!("Tueの値は{:?}", wk1); println!("Wedの値は{:?}", wk2); }
$ ./main
Monの値はMon
Tueの値はTue
Wedの値はThu
rustだと、c++のように0,1,2…ではなく、値がそのまま出てくる。
### Option
取得できないかもしれない値を表現する列挙型
値が無いことを示すNoneと、あることを示すSome(T)のどちらかを取る
pub enum Option<T> { None, Some(T), }
fn get_value_bad(v: bool, result: &mut usize) -> bool { if v { *result = 100; true } else { false } } fn get_value_good(v: bool) -> Option<usize> { if v { Some(100) } else { None } } fn main(){ let mut result = 0; if get_value_bad(true, &mut result){ println!("success: {}", result); } else { println!("failure"); } match get_value_good(true){ Some(result) => println!("success: {}", result), None => println!("failure") } }
$ cargo new hello –bin
use std::net::TcpListener; fn main() { let listner = TcpListener::bind("127.0.0.1:7878").unwrap(); for stream in listner.incoming() { let stream = stream.unwrap(); println!("Connection established!"); } }
ターミナルから、リクエストを送信する
$ ping http://127.0.0.1:7878/
ping: http://127.0.0.1:7878/: Name or service not known
すると、サーバ側では、
Running `target/debug/hello`
Connection established!
Connection established!
Connection established!
Connection established!
…
use std::io::prelude::*; use std::net::TcpStream; use std::net::TcpListener; fn main() { let listner = TcpListener::bind("127.0.0.1:7878").unwrap(); for stream in listner.incoming() { let stream = stream.unwrap(); handle_connection(stream); } } fn handle_connection(mut stream: TcpStream) { let mut buffer = [0; 1024]; stream.read(&mut buffer).unwrap(); println!("Request: {}", String::from_utf8_lossy(&buffer[..])); }
fn handle_connection(mut stream: TcpStream) { let mut buffer = [0; 1024]; stream.read(&mut buffer).unwrap(); let mut file = File::open("hello.html").unwrap(); let mut contents = String::new(); file.read_to_string(&mut contents).unwrap(); let response = format!("HTTP/1.1 200 OK\r\n\r\n", contents); stream.write(response.as_bytes()).unwrap(); stream.flush().unwrap(); }
fn handle_connection(mut stream: TcpStream) { let mut buffer = [0; 1024]; stream.read(&mut buffer).unwrap(); let mut file = File::open("hello.html").unwrap(); let mut contents = String::new(); file.read_to_string(&mut contents).unwrap(); let response = format!("HTTP/1.1 200 OK\r\n\r\n{}", contents); stream.write(response.as_bytes()).unwrap(); stream.flush().unwrap(); }
これで、curl http://127.0.0.1:7878を打つと、htmlが返ってくる
fn handle_connection(mut stream: TcpStream) { let mut buffer = [0; 1024]; stream.read(&mut buffer).unwrap(); let get = b"GET / HTTP/1.1\r\n"; if buffer.starts_with(get) { let mut file = File::open("hello.html").unwrap(); let mut contents = String::new(); file.read_to_string(&mut contents).unwrap(); let response = format!("HTTP/1.1 200 OK\r\n\r\n{}", contents); stream.write(response.as_bytes()).unwrap(); stream.flush().unwrap(); } else { let status_line = "HTTP/1.1 404 NOT FOUND\r\n\r\n"; let mut file = File::open("404.html").unwrap(); let mut contents = String::new(); file.read_to_string(&mut contents).unwrap(); let response = format!("{} {}", status_line, contents); stream.write(response.as_bytes()).unwrap(); stream.flush().unwrap(); } }
### 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("src/test_input.json") .expect("JSON Read Failed."); let deserialized: Vec<CharaParam> = serde_json::from_str(&input_fn).unwrap(); for data in &deserialized { println!("{:?}", data); } }
### 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("src/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(()) }
serde_json::to_string(&charaparam_list).unwrap(); でstring型にしていますね。
つまりこういうことかな?
use serde::{Serialize, Deserialize}; use std::io::prelude::*; use hex_literal::hex; use k256::{ecdsa::{SigningKey, Signature, signature::Signer, signature::Verifier, VerifyingKey}}; use chrono::{Utc, Local, DateTime, Date}; #[derive(Serialize, Deserialize, Debug)] struct UnsignedTransaction { time: String, sender: String, receiver: String, amount: i32, } #[derive(Serialize, Deserialize, Debug)] struct SignedTransaction { time: String, sender: String, receiver: String, amount: i32, signature: String, } fn hex(bytes: &[u8]) -> String { bytes.iter().fold("".to_owned(), |s, b| format!("{}{:x}", s, b)) } fn main(){ let private_key: SigningKey = SigningKey::from_bytes(&hex!( "DCDFF4B7CA287CC7BD30ECAEF0622265DB4E14054E12954225457C3A6B84F135" ).into()).unwrap(); let public_key: &VerifyingKey = private_key.verifying_key(); let public_key_str = hex(&public_key.to_encoded_point(false).to_bytes()); let public_key_b_str = "4bac6cb0f4ad6397752c3d73b88c5c86e3d88ac695118494a1732e2abd16c76acad3d6586c37c8db7e69c2f812f99275198936957d72c38d71981991124"; let utc_datetime: DateTime<Utc> = Utc::now(); let ut1 = UnsignedTransaction {time: utc_datetime.to_string(), sender: public_key_str.to_string(), receiver: public_key_b_str.to_string(), amount: 10}; println!("{:?}", ut1); let serialized: String = serde_json::to_string(&ut1).unwrap(); let sig1: Signature = private_key.sign(serialized.as_bytes()); let signed_ut1 = SignedTransaction {time: utc_datetime.to_string(), sender: public_key_str.to_string(), receiver: public_key_b_str.to_string(), amount: 10, signature: sig1.to_string()}; println!("{:?}", signed_ut1); }
UnsignedTransaction { time: “2024-12-22 00:30:45.843926032 UTC”, sender: “4bac6cb0f4ad6397752c3d73b88c5c86e3d88ac695118494a1732e2abd16c76acad3d6586c37c8db7e69c2f812f99275198936957d72c38d71981991123”, receiver: “4bac6cb0f4ad6397752c3d73b88c5c86e3d88ac695118494a1732e2abd16c76acad3d6586c37c8db7e69c2f812f99275198936957d72c38d71981991124”, amount: 10 }
SignedTransaction { time: “2024-12-22 00:30:45.843926032 UTC”, sender: “4bac6cb0f4ad6397752c3d73b88c5c86e3d88ac695118494a1732e2abd16c76acad3d6586c37c8db7e69c2f812f99275198936957d72c38d71981991123”, receiver: “4bac6cb0f4ad6397752c3d73b88c5c86e3d88ac695118494a1732e2abd16c76acad3d6586c37c8db7e69c2f812f99275198936957d72c38d71981991124”, amount: 10, signature: “301DC132F45B1F8D0734A51E73F6725260B5365FBC9C2C405B9A12C70B97E47842366C2EC589F05AF8715268BC5A954B487F234F5FD3157ADBE8940AF2037B60” }
シリアライズしてバイトにしてsignする。。
しかし、これだとうまくいかん…
let pubkey = signed_ut1.sender; let sig = signed_ut1.signature; let verified = pubkey.verify(ut1, &sig).is_ok(); if verified { println!("文章は改竄されていません。"); } else { println!("文章が改竄されています。"); }
#![feature(catch_expr)] use std::fs::File; use std::io::{self, BufReader, Read}; fn main(){ try { let f = File::open("foo.txt")?; let mut f = BufReader::new(f); let mut buf = String::new(); f.read_to_string(&mut buf)?; println!("{}", buf); Ok(()) }.unwrap_or_else(|err: io::Error| { eprintln!("An error occured: {}", err); }) }
try & catchではなく、matchで実装する
fn main(){ let file_path = "foo.txt"; match std::fs::remove_file(file_path) { Ok(()) => {}, Err(e) => { eprintln!("Failed deleting file{}. Error caut:", file_path); // return Err(e); } } }
$ ./main
Failed deleting filefoo.txt. Error caut:
true or falseの場合はifで大丈夫
fn main(){ let private_key: SigningKey = SigningKey::from_bytes(&hex!( "DCDFF4B7CA287CC7BD30ECAEF0622265DB4E14054E12954225457C3A6B84F135" ).into()).unwrap(); let public_key: &VerifyingKey = private_key.verifying_key(); let message = b"hello, world"; let signature: Signature = private_key.sign(message); let verified = public_key.verify(message, &signature).is_ok(); println!("private key: {:X?}", hex(&private_key.to_bytes())); println!("public key: {:X?}", hex(&public_key.to_encoded_point(false).to_bytes())); println!("Signature: {:X?}", hex(&signature.to_bytes())); if verified { println!("文章は改竄されていません。"); } else { println!("文章が改竄されています。"); } }
private key: “dcdff4b7ca287cc7bd30ecaef0622265db4e1454e12954225457c3a6b84f135”
public key: “4bac6cb0f4ad6397752c3d73b88c5c86e3d88ac695118494a1732e2abd16c76acad3d6586c37c8db7e69c2f812f99275198936957d72c38d71981991123”
Signature: “f690afffd24623627bfa97996873a6a9d8ca8e9bb7ab6ad269c929453b42985573cdc4c05b8522224065757fef6c181c8418ffd3debdab4be22a73b2e5b6ce”
文章は改竄されていません。
fn hex(bytes: &[u8]) -> String { bytes.iter().fold("".to_owned(), |s, b| format!("{}{:x}", s, b)) } fn main(){ let s = hex(&[10, 32, 123]); println!("{:?}", s); }
$ ./main
“a207b”
これを秘密鍵、公開鍵のコードに実装します。
use hex_literal::hex; use k256::{ecdsa::{SigningKey, Signature, signature::Signer, signature::Verifier, VerifyingKey}}; fn hex(bytes: &[u8]) -> String { bytes.iter().fold("".to_owned(), |s, b| format!("{}{:x}", s, b)) } fn main(){ let private_key: SigningKey = SigningKey::from_bytes(&hex!( "DCDFF4B7CA287CC7BD30ECAEF0622265DB4E14054E12954225457C3A6B84F135" ).into()).unwrap(); let public_key: &VerifyingKey = private_key.verifying_key(); let message = b"hello, world"; let signature: Signature = private_key.sign(message); let verified = public_key.verify(message, &signature).is_ok(); println!("private key: {:X?}", hex(&private_key.to_bytes())); println!("public key: {:X?}", hex(&public_key.to_encoded_point(false).to_bytes())); println!("message: {:X?}", message); println!("Signature: {:X?}", hex(&signature.to_bytes())); println!("verified: {:X?}", verified); }
private key: “dcdff4b7ca287cc7bd30ecaef0622265db4e1454e12954225457c3a6b84f135”
public key: “4bac6cb0f4ad6397752c3d73b88c5c86e3d88ac695118494a1732e2abd16c76acad3d6586c37c8db7e69c2f812f99275198936957d72c38d71981991123”
message: [68, 65, 6C, 6C, 6F, 2C, 20, 77, 6F, 72, 6C, 64]
Signature: “f690afffd24623627bfa97996873a6a9d8ca8e9bb7ab6ad269c929453b42985573cdc4c05b8522224065757fef6c181c8418ffd3debdab4be22a73b2e5b6ce”
verified: true
うん、いい感じ
$ cargo add hex-literal k256
[dependencies] chrono = "0.4.23" hex-literal = "0.4.1" k256 = "0.13.4"
use hex_literal::hex; use k256::{ecdsa::{SigningKey, Signature, signature::Signer, signature::Verifier, VerifyingKey}}; fn main(){ let private_key: SigningKey = SigningKey::from_bytes(&hex!( "DCDFF4B7CA287CC7BD30ECAEF0622265DB4E14054E12954225457C3A6B84F135" ).into()).unwrap(); let public_key: &VerifyingKey = private_key.verifying_key(); let message = b"hello, world"; let signature: Signature = private_key.sign(message); let verified = public_key.verify(message, &signature).is_ok(); println!("private key: {:X?}", private_key.to_bytes()); println!("public key: {:X?}", public_key.to_encoded_point(false).to_bytes()); println!("message: {:X?}", message); println!("Signature: {:X?}", signature.to_bytes()); println!("verified: {:X?}", verified); }
private key: [DC, DF, F4, B7, CA, 28, 7C, C7, BD, 30, EC, AE, F0, 62, 22, 65, DB, 4E, 14, 5, 4E, 12, 95, 42, 25, 45, 7C, 3A, 6B, 84, F1, 35]
public key: [4, BA, C6, CB, 0, F, 4, AD, 63, 97, 7, 52, C3, D7, 3B, 88, C5, C8, 6E, 3D, 88, AC, 69, 51, 18, 49, 4A, 1, 73, 2E, 2A, BD, 16, C7, 6A, CA, D3, D6, 58, 6C, 37, C8, DB, 7E, 69, C2, F8, 12, F9, 92, 75, 19, 89, 36, 95, 7D, 72, C3, 8D, 71, 9, 81, 99, 11, 23]
message: [68, 65, 6C, 6C, 6F, 2C, 20, 77, 6F, 72, 6C, 64]
Signature: [F6, 90, AF, FF, D2, 46, 23, 62, 7B, FA, 97, 99, 68, 73, A6, A9, D8, CA, 8E, 9B, B7, AB, 6A, D2, 69, C9, 29, 45, 3B, 42, 98, 55, 73, CD, C4, C0, 5B, 85, 22, 22, 40, 65, 75, 7F, EF, 6C, 18, 1, C8, 41, 8F, FD, 3D, EB, DA, B, 4B, E2, 2A, 73, B2, E5, B6, CE]
verified: true
### トランザクション
use std::collections::HashMap; fn main(){ let transaction = HashMap::from([ ("Time", "2024-12-20 13:58"), ("sender", "A"), ("receiver", "B"), ("amount", "1") ]); for (k, v) in &transaction { println!("{} {}", k, v); } }
Hashmapだとkey, valueが全て同じ型になってしまうので、構造体を使用する。
#[derive(Debug)] struct Transaction { time: String, sender: String, receiver: String, amount: i32, } fn main(){ let t1 = Transaction {time: "2024-12-20 13:58".to_string(), sender: "A".to_string(), receiver: "B".to_string(), amount: 10}; println!("{:?}", t1); }
$ ./main
Transaction { time: “2024-12-20 13:58”, sender: “A”, receiver: “B”, amount: 10 }
複数トランザクションの場合は配列を使用する
fn main(){ let t1 = Transaction {time: "2024-12-20 13:58".to_string(), sender: "A".to_string(), receiver: "B".to_string(), amount: 10}; let t2 = Transaction {time: "2024-12-21 13:58".to_string(), sender: "B".to_string(), receiver: "C".to_string(), amount: 5}; let transactions = [t1, t2]; println!("{:?}", transactions); }
### Rustで日時を扱う
use chrono::{Utc, Local, DateTime, Date}; fn main(){ let utc_datetime: DateTime<Utc> = Utc::now(); let utc_date: Date<Utc> = Utc::today(); println!("{}", utc_datetime); println!("{}", utc_date); }
2024-12-20 05:46:43.775637762 UTC
2024-12-20UTC
crateを使うには、Cargo.tomlを利用する
[package] name = "hoge" version = "0.1.0" edition = "2021" [dependencies] chrono = "0.4.23"
凄い基本的なことを書くだけでも時間がかかりますな。。
use chrono::{Utc, Local, DateTime, Date}; #[derive(Debug)] struct Transaction { time: String, sender: String, receiver: String, amount: i32, } fn main(){ let utc_datetime: DateTime<Utc> = Utc::now(); let t1 = Transaction {time: utc_datetime.to_string(), sender: "A".to_string(), receiver: "B".to_string(), amount: 10}; let t2 = Transaction {time: utc_datetime.to_string(), sender: "B".to_string(), receiver: "C".to_string(), amount: 5}; let transactions = [t1, t2]; println!("{:?}", transactions); }
[Transaction { time: “2024-12-21 06:09:35.567867379 UTC”, sender: “A”, receiver: “B”, amount: 10 }, Transaction { time: “2024-12-21 06:09:35.567867379 UTC”, sender: “B”, receiver: “C”, amount: 5 }]