$ cargo new –lib my_package
src/lib.rs
pub fn hello(name: &str) { println!("Hello, {}", name); }
src/bin/bin.rs
use my_package::hello; fn main() { hello("bin_1"); }
$ cargo run –bin bin
随机应变 ABCD: Always Be Coding and … : хороший
$ cargo new –lib my_package
src/lib.rs
pub fn hello(name: &str) { println!("Hello, {}", name); }
src/bin/bin.rs
use my_package::hello; fn main() { hello("bin_1"); }
$ cargo run –bin bin
これはかなり面白い。というか、参考にさせていただきます!
use std::{collections::HashMap, fs::OpenOptions}; use chrono::NaiveDate; use clap::{Args, Parser, Subcommand}; use csv::{Writer, Reader, WriterBuilder}; use serde::{Deserialize, Serialize}; #[derive(Parser)] #[clap(version = "1.0")] struct App { #[clap(subcommand)] command: Command, } #[derive(Subcommand)] enum Command { /// 新しい口座を作る New(NewArgs), /// 口座に入金する Deposit(DepositArgs), /// 口座から出金する Withdraw(WithdrawArgs), /// CSVからインポートする Import(ImportArgs), /// レポートを出力する Report(ReportArgs), } #[derive(Args)] struct NewArgs { account_name: String, } impl NewArgs { fn run(&self) { let file_name = format!("{}.csv", self.account_name); let mut writer = Writer::from_path(file_name).unwrap(); writer .write_record(["日付", "用途", "金額"]) .unwrap(); writer.flush().unwrap(); } } #[derive(Args)] struct DepositArgs { account_name: String, date: NaiveDate, usage: String, amount: u32, } impl DepositArgs { fn run(&self) { let open_option = OpenOptions::new() .write(true) .append(true) .open(format!("{}.csv", self.account_name)) .unwrap(); let mut writer = Writer::from_writer(open_option); writer .write_record([ self.date.format("%Y-%m-%d").to_string(), self.usage.to_string(), self.amount.to_string()]) .unwrap(); writer.flush().unwrap(); } } #[derive(Args)] struct WithdrawArgs { account_name: String, date: NaiveDate, usage: String, amount: u32, } impl WithdrawArgs { fn run(&self) { let open_option = OpenOptions::new() .write(true) .append(true) .open(format!("{}.csv", self.account_name)) .unwrap(); let mut writer = Writer::from_writer(open_option); writer .write_record([ self.date.format("%Y-%m-%d").to_string(), self.usage.to_string(), format!("-{}", self.amount), ]) .unwrap(); writer.flush().unwrap(); } } #[derive(Args)] struct ImportArgs { src_file_name: String, dst_account_name: String, } impl ImportArgs { fn run(&self) { let open_option = OpenOptions::new() .write(true) .append(true) .open(format!("{}.csv", self.dst_account_name)) .unwrap(); let mut writer = WriterBuilder::new() .has_headers(false) .from_writer(open_option); let mut reader = Reader::from_path(&self.src_file_name).unwrap(); for result in reader.deserialize() { let record: Record = result.unwrap(); writer.serialize(record).unwrap(); } } } #[derive(Serialize, Deserialize)] struct Record { 日付: NaiveDate, 用途: String, 金額: i32, } #[derive(Args)] struct ReportArgs { files: Vec<String>, } impl ReportArgs { fn run(&self) { let mut map = HashMap::new(); for file in &self.files { let mut reader = Reader::from_path(file).unwrap(); for result in reader.records() { let record = result.unwrap(); let amount : i32 = record[2].parse().unwrap(); let date: NaiveDate = record[0].parse().unwrap(); let sum = map.entry(date.format("%Y-%m").to_string()) .or_insert(0); *sum += amount; } } println!("{:?}", map); } } fn main() { let args = App::parse(); match args.command { Command::New(args) => args.run(), Command::Deposit(args) => args.run(), Command::Withdraw(args) => args.run(), Command::Import(args) => args.run(), Command::Report(args) => args.run(), } }
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); }
Running `target/debug/kakeibo arg1 arg2`
Hello arg1 via target/debug/kakeibo!
use clap::Parser; #[derive(Parser)] #[clap(version = "1.0")] struct Args { arg1: String, arg2: String } fn main() { let _args = Args::parse(); }
use clap::{Parser, Subcommand}; #[derive(Parser)] #[clap(version = "1.0")] struct App { #[clap(subcommand)] command: Command, } #[derive(Subcommand)] enum Command { /// 新しい口座を作る New, /// 口座に入金する Deposit, /// 口座から出金する Withdraw, /// CSVからインポートする Import, /// レポートを出力する Report, } fn main() { let _args = App::parse(); }
$ ./target/debug/kakeibo -h
Usage: kakeibo
Commands:
new 新しい口座を作る
deposit 口座に入金する
withdraw 口座から出金する
import CSVからインポートする
report レポートを出力する
help Print this message or the help of the given subcommand(s)
Options:
-h, –help Print help
-V, –version Print version
use std::io::stdin; fn main() { let mut memory = Memory { slots: vec![0.0; 10], } let mut prev_result: f64 = 0.0; for line in stdin().lines() { let line = line.unwrap(); if line.is_empty() { break; } let tokens: Vec<&str> = line.split(char::is_whitespace).collect(); let is_memory = tokens[0].starts_with("mem"); if is_memory && tokens[0].ends_with('+') { add_and_print_memory(&mut memory, tokens[0], prev_result); continue; } else if is_memory && tokens[0].ends_with('-') { add_and_print_memory(&mut memory, tokens[0], -prev_result); continue; } let left = eval_token(tokens[0], &memory); let right = eval_token(tokens[2], &memory); let result = eval_expression(left, tokens[1], right); print_output(result); prev_result = result; } } struct Memory { slots: Vec<f64>, } fn add_and_print_memory(memory: &mut Memory, token: &str, prev_result: f64) { let slot_index: usize = token[3..token.len() - 1].parse().unwrap(); memory.slots[slot_index] += prev_result; print_output(memory.slots[slot_index]); } fn eval_token(token: &str, memory: &Memory) -> f64 { if token.starts_with("mem") { let slot_index: usize = token[3..].parse().unwrap(); memory.slots[slot_index] } else { token.parse().unwrap() } } fn eval_expression(left: f64, operator: &str, right: f64) -> f64 { match operator { "+" => left + right, "-" => left - right, "*" => left * right, "/" => left / right, _ => { unreachable!() } } } fn print_output(value: f64) { println!(" => {}", value); }
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.49s
Running `target/debug/app`
1 + 2
=> 3
mem1+
=> 3
3 + 4
=> 7
mem2-
=> -7
mem1 * mem2
=> -21
このソース見た時、凄い感慨したんよね。絵柄と1~13の数字でdeckを作るところ。
pocker chaseの裏側ってどうやってんだろうずっと思ってたんだけど、これを見ると、カードの配り方や、最後の役完成時の判定をどうやってるかがわかる。素晴らしい!
use rand::seq::SliceRandom; #[derive(Debug, Clone, Copy, PartialEq)] enum Suit { Club, Diamond, Heart, Spade, } #[derive(Debug, Clone, Copy, PartialEq)] struct Card { suit: Suit, rank: i32, } fn main() { let mut deck: Vec<Card> = Vec::new(); let suits = [Suit::Club, Suit::Diamond, Suit::Heart, Suit::Spade]; for suit in suits { for rank in 1..=13 { deck.push(Card {suit, rank}); } } let mut rng = rand::thread_rng(); deck.shuffle(&mut rng); let mut hand: Vec<Card> = Vec::new(); for _ in 0..5 { hand.push(deck.pop().unwrap()); } hand.sort_by(|a, b| a.rank.cmp(&b.rank)); for (i, card) in hand.iter().enumerate() { println!("{:}: {:?} {:}", i +1, card.suit, card.rank); } println!("入れ替えたいカードの番号を入力してください(例: 1 2 3)"); let mut input = String::new(); std::io::stdin().read_line(&mut input).unwrap(); let numbers : Vec<usize> = input.split_whitespace() .map(|x| x.parse().unwrap()) .collect::<Vec<usize>>(); for number in numbers { hand[number - 1] = deck.pop().unwrap(); } hand.sort_by(|a, b| a.rank.cmp(&b.rank)); for (i, card) in hand.iter().enumerate() { println!("{:}: {:?} {:}", i +1, card.suit, card.rank); } let suit = hand.first().unwrap().suit; let flash = hand.iter().all(|c| c.suit == suit); let mut count = 0; for i in 0..hand.len() - 1 { for j in i + 1..hand.len() { if hand[i].rank == hand[j].rank { count += 1; } } } if flash { println!("flash!"); } else if count >= 3 { println!("スリーカード!"); } else if count == 2 { println!("2ペア"); } else if count == 1 { println!("1ペア"); } else { println!("役なし..."); } }
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.62s
Running `target/debug/app`
1: Spade 3
2: Spade 4
3: Club 5
4: Diamond 8
5: Spade 13
入れ替えたいカードの番号を入力してください(例: 1 2 3)
1 2 3 4
1: Diamond 10
2: Club 10
3: Diamond 12
4: Heart 12
5: Spade 13
2ペア
fn main() { println!("1 + 1 = ??"); println!("??の値を入力してください。"); let mut ans_input = String::new(); std::io::stdin().read_line(&mut ans_input).unwrap(); dbg!(ans_input); }
Compiling app v0.1.0 (/home/vagrant/dev/rust/app)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.43s
Running `target/debug/app`
1 + 1 = ??
??の値を入力してください。
2
[src/main.rs:6:5] ans_input = “2\n”
fn main() { let value = check_negative(-32); println!("{:?}", value); let value = check_negative(10); println!("{:?}", value); } fn check_negative(num: i32) -> Result<i32, Box<dyn std::error::Error>> { if num < 0 { return Err("負の値です".into()); } Ok(num) }
Compiling app v0.1.0 (/home/vagrant/dev/rust/app)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.40s
Running `target/debug/app`
Err(“負の値です”)
Ok(10)
Box
Box: ヒープ上にデータを格納するためのスマートポインタ
dyn: dynamicの略。実行時にトレイトの具体的な型を決めたいときに使う
error::Error: Rustの標準ライブラリのerrorモジュールにあるErrorトレイト
エラートレイトの何らかの値を返す
Resultを理解すると、エラー処理の書き方が変わってきますね。
Option型は値がない可能性を示す。
fn main() { let mut values: Vec<Option<i32>> = Vec::new(); values.push(Some(777)); values.push(None); for value in values { match value { Some(_) => println!("It's threre, {}", value.unwrap()), None => println!("No value, it's None"), } } }
Compiling app v0.1.0 (/home/vagrant/dev/rust/app)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.25s
Running `target/debug/app`
It’s threre, 777
No value, it’s None
Option型を使う場合は、Some、Noneもセットで使うのね。
なるほど〜、結構基本的なところだな〜
演算子を前に置くのをポーランド記法という 4 + 5 * 8 – 9 / 3 は、「- + 4 * 5 8 / 9 3」っと書く。
逆に、演算子を後ろに置くのを逆ポーランド記法という。「4 5 8 * + 9 3 / -」と書く。
逆ポーランド記法はスタックで処理しやすく、色々な場面で使われる。
fn calc(expression: String) { let mut stack: Vec<i32> = Vec::new(); for i in expression.split(' ') { println!("{:?}", stack); if i == '+'.to_string() { let b = stack.pop().unwrap(); let a = stack.pop().unwrap(); stack.push(a + b); } else if i == '-'.to_string() { let b = stack.pop().unwrap(); let a = stack.pop().unwrap(); stack.push(a - b); } else if i == '*'.to_string() { let b = stack.pop().unwrap(); let a = stack.pop().unwrap(); stack.push(a * b); } else if i == '/'.to_string() { let b = stack.pop().unwrap(); let a = stack.pop().unwrap(); stack.push(a / b); } else { stack.push(i.parse::<i32>().unwrap()); } } println!("{:?}", stack); } fn main() { calc("4 6 2 + * 3 1 - 5 * -".to_string()); }
[]
[4]
[4, 6]
[4, 6, 2]
[4, 8]
[32]
[32, 3]
[32, 3, 1]
[32, 2]
[32, 2, 5]
[32, 10]
[22]
なるほど、これは面白い。
コンパイラのOP_CODEでも同じようなことをやったような気がする。
use std::collections::HashMap; fn main() { let text = ['J','A','P','A','N','H','O','L','D','I','N','G',]; let pattern = ['H','O','L','D']; let mut skip = HashMap::new(); for i in 0..(pattern.len() - 1) { skip.insert( pattern[i], pattern.len() - i - 1, ); } println!("{:?}", skip); let mut i = pattern.len() - 1; while i < text.len() { let mut flg = true; for j in 0..pattern.len() { if text[i-j] != pattern[pattern.len()-1-j] { flg = false; break; } } if flg == true { println!("{}", i - pattern.len() + 1); break; } else if skip.get(&text[i]).is_some(){ i += skip[&text[i]]; } else { i += pattern.len(); } } }
Compiling rust v0.1.0 (/home/vagrant/dev/algorithm/rust)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.24s
Running `target/debug/rust`
{‘H’: 3, ‘O’: 2, ‘L’: 1}
5