まず Power shell をダブルクリックで起動できるようにしろだぜ☆
[ここを読む(https://qiita.com/muzudho1/items/3a5784faddfa77df99f9)
設定ファイルを探して設定しろ。
ディレクトリ構成を合わせろ。
リリース・ビルドだけでなく、デバッグ・ビルドも持っとけ。
リリース・ビルドで強制終了したら エラー原因が分からん。ログを吐く .exe 必須。
ログ吐く前に 強制終了してたら 設定ファイル読み込めてない可能性高い。
target
|
+-- release
|
+-- debug
特に設定していなければ rust の場合 cargo build
で target/debug ディレクトリの下に .exe や .d が出力されている。
cargo build --release
なら target/release。
.d はテキストファイルで開けば分かるが デバッグで使ってるだけなんで本番環境で要らない。
人間がボトルネック
当日の修正でバグを盛り込んでしまう。結果を見る前から 修正 という言葉を使うのは矛盾している。修正ではなく変更。
感情がない人というのは 1つ1つの感情表現が豊かな人に比べて 感情を細かく多めに使ってしまって 平坦になっている人のことだ。
ディレクトリー構成
テスト環境と、本番環境では ディレクトリーのここに置け、という制約が変わることがある。
だいたいConfigみたいな感じの名前の付いている設定ファイルがあると思うが、設定しても、
管理者権限や、パーミッションを別途 要求される場合があるので まず ドキュメント・ディレクトリーに配置して
そもそも 動くのか確認する。
全部絶対ファイルパスで書いてみる。
古いプログラムが動いていて、新しいプログラムが動かないのなら
プログラム言語の環境を 再インストール、アップデートだ!
rustup update
rustc --version
これでバージョンがそろってて、あとは何が違うのか。
調べてみると、 debug ビルドのものは動いて、 release ビルドのものは動かない。
たどっていくと、ディレクトリ構成が異なるのを認識し、
logs
フォルダーが無いと強制終了するようになっている。
/// Write line.
pub fn println(&self, line: &str) {
println!("{}", line);
let mut file = OpenOptions::new()
.create(true)
.write(true)
.append(true)
.open(&self.log_file_name)
.unwrap();
if let Err(e) = writeln!(file, "{}", line) {
eprintln!("Couldn't write to file: {}", e);
}
}
ファイルが無ければ上書きするが、ディレクトリーがない場合は .unwrap() で強制終了するのでエラーも吐かない。
/// Write line.
pub fn println(&self, line: &str) {
println!("{}", line);
// ディレクトリー作成。
if let Some(parent) = Path::new(&self.log_file_name).parent() {
match fs::create_dir_all(parent) {
Ok(_x) => {}
Err(err) => panic!(err),
}
} else {
panic!("Create directory fail. {}", self.log_file_name);
}
let mut file = OpenOptions::new()
.create(true)
.write(true)
.append(true)
.open(&self.log_file_name)
.unwrap();
if let Err(e) = writeln!(file, "{}", line) {
eprintln!("Couldn't write to file: {}", e);
}
}
OpenOption をぜんぶ検索して ディレクトリー作成コードを付けまくって応急処置。
.unwrap()
とか適当に使ってると エラーが出ない。
.unwrap();
を機械的に .unwrap_or_else(|| panic!(""));
に置き換えるとどうだろうか?
それでもログに残らないので、ログに残るように改造する。
panic! の代わりに形式上 String を返す関数を作り、その中で ログ取って、panic! する。
と思ったら、 unwrap_or_else
の仕様はメソッドによってバラバラなようだ。
let ext = get_extension_from_filename(&in_file)
.unwrap_or_else(|| app.comm.panic_as_str("Fail. get_extension_from_filename."))
.to_uppercase();
雰囲気的にはこんな感じで、戻り値の違うログ取りパニック関数を用意していく。
unwrap_or_panic()
が欲しい。戻り値の種類は 有限だが山ほどある。
/// panic! で包んで使う。
pub fn panic(&self, msg: &str) -> String {
self.println(msg); // コンソールに表示して、ログも取る。
String::from(msg)
}
こういう自作関数を作る。
let file = args
.output_file
.unwrap_or_else(|| panic!(app.comm.panic("Fail. args.output_file.")));
こんな雰囲気で使う。
Result
型の .unwrap() を .unwrap_or_else() に変えるとなんかエラーが出る。
/// 入出力エラーの時、 panic! で包んで使う。
pub fn panic_io(&self, err: &std::io::Error) -> String {
let msg = format!("{}", err);
self.println(&msg);
String::from(msg)
}
こうすりゃいいのか。
しかし Error クラスはいっぱいある。 int型のパースの Error も別クラス。
rust のエラークラスの操作は統一されてないのか?
unwrap_or_else(|f| panic!(app.comm.panic(&f.to_string())))
.to_string()
できるやつはこれでいける。
let id: i8 = id_vec[i].parse().unwrap();
文字列、数値変換のときのパース失敗時のパニックのメッセージをログに取るのがむずかしい。
let id: i8 = match id_vec[i].parse() {
Ok(x) => x,
Err(err) => panic!(app.comm.println(&format!("{}", id_vec[i]))),
};
数値変換のたびに こんな長いの書きたくない。
let id: i8 = id_vec[i]
.parse()
.unwrap_or_else(|err| panic!(app.comm.println(&format!("{}", err))));
短くしてこうか。
100個ほどの '.unwrap()' を '.unwrap(' で検索し、すべて '.unwrap_or_else' で書き直し
ログ出力するように2時間ほどで書き直し。
本番環境で release フォルダーに入れていたので ファイルパスが違っていたことが分かったので
release フォルダーの外に出したら動いた。