だらだら書く。この記事は 誰も読んでないので たまにこっそり書き換える。
説明書も読まず 適当に書いている。わたしには Rust言語 なんか分からん。
Option と Result とか知らんよな。 unwrap() とか expect() も意味分からんし。
Rust言語には ヌル が無い。
Java使いのわたしにはこれはありがたいことで、 nullと "" を区別することが無くなる。楽だ。
じゃあ 検索結果がないときとか、どうするのか。
言語仕様として 検索結果がないことを判定できればいいわけで、ヌルとか要らないんだぜ。
Rust言語では 有るのか、無いのか というとき、 Option というのを使う。 以下の2択で、
Some(some), None
match 文と組み合わせると 以下のようになる。
match banana_box {
Some(banana) => print!(banana),
None => panic!("I want a banana.")
}
None の判定とか要らないんで 中身の banana だけ欲しいときは 以下のようにする。
let banana = banana_box.unwrap();
同じように、 正常に終わったか、エラーだったのか というとき、 Result を使う。
Result というのは 次の2択だ。
Ok(some), Err(e)
match 文と組み合わせると 以下のようになる。
match mix_banana_juice() {
Ok(banana) => print!("Banana is good."),
Err(err) => panic!("An error has occurred.", err)
}
同じように、エラー処理 要らないんで 中身のバナナだけ欲しいときは 以下のようにする。
let banana = mix_banana_juice().unwrap();
いや、メッセージぐらい出して終了して欲しいときは .unwrap()の代わりに .expect() を使う。以下のようにする。
let banana = mix_banana_juice().expect("I dropped a banana.");
mut とか &mut とか 何なんだろな。
C# 言語を触っていると get や set があるのは 分かるんだが、 Rust 言語はまた違う。
get、get_mut、set の3つはある感じ。
pub struct Fruit {
/// バナナがあるとする。
banana: Banana
}
impl Fruit {
/// 読取専用のバナナを取得する。
pub fn get_banana(&self) -> &Banana {
&self.banana
}
/// 変更できるバナナを取得する。
pub fn get_mut_banana(&mut self) -> &mut Banana {
&mut self.banana
}
/// バナナをセットする。
pub fn set_banana(&mut self, value: Banana) {
self.banana = value
}
}
C# 言語は メンバーの参照を 外に渡すと public だの private だのというアクセス制限は無意味になるが、
Rust 言語では 言語仕様でずっと それが変更できるのか、できないのか追跡する。
なんか適当に。
例えば次のコードは cargo clippy
でコンパイルが通ったコードの一部分だぜ。
pub struct Response<T> {
pub caret: usize,
pub done_line: bool,
pub quits: bool,
pub groups: Box<Vec<String>>,
pub next: &'static str,
pub linebreak_controller_changed: bool,
pub linebreak_controller: Controller<T>,
}
pub trait ResponseAccessor<T> {
fn get_caret(&self) -> usize;
fn set_caret(&mut self, usize);
fn is_done_line(&self) -> bool;
fn set_done_line(&mut self, bool);
fn is_quits(&self) -> bool;
fn set_quits(&mut self, bool);
fn get_groups(&self) -> &Box<Vec<String>>;
fn set_groups(&mut self, Box<Vec<String>>);
fn get_next(&self) -> &'static str;
fn set_next(&mut self, &'static str);
fn get_linebreak_controller_changed(&self) -> bool;
fn set_linebreak_controller_changed(&mut self, bool);
fn get_linebreak_controller(&self) -> Controller<T>;
fn set_linebreak_controller(&mut self, Controller<T>);
}
impl<T> ResponseAccessor<T> for Response<T> {
fn get_caret(&self) -> usize {
self.caret
}
fn set_caret(&mut self, caret2:usize) {
self.caret = caret2
}
fn is_done_line(&self) -> bool {
self.done_line
}
fn set_done_line(&mut self, done_line2:bool) {
self.done_line = done_line2
}
fn is_quits(&self) -> bool {
self.quits
}
fn set_quits(&mut self, quits2:bool) {
self.quits = quits2
}
fn get_groups(&self) -> &Box<Vec<String>> {
&self.groups
}
fn set_groups(&mut self, groups:Box<Vec<String>>) {
self.groups = groups
}
fn get_next(&self) -> &'static str {
&self.next
}
fn set_next(&mut self, next2:&'static str) {
self.next = next2
}
fn get_linebreak_controller_changed(&self) -> bool {
self.linebreak_controller_changed
}
fn set_linebreak_controller_changed(&mut self, value:bool) {
self.linebreak_controller_changed = value
}
fn get_linebreak_controller(&self) -> Controller<T> {
self.linebreak_controller
}
fn set_linebreak_controller(&mut self, value:Controller<T>) {
self.linebreak_controller = value
}
}
struct
は分かるだろ。 C言語から ずっとある。
次に trait
。 一見すると C# の interface みたいなもんかと思ってしまうが違う。
impl
と impl トレイト名 for ストラクト名
もまた違う。
Box<名前>
と Box<dyn ストラクト名>
もまた違う。ただ、ボックスというのは多分
固定長メモリの ポインターが入っているのだろう。
可変長のものを 固定長にしたいときに使う。
あとなんか分かったら書き足す。
pub fn new()->名前
の作り方もうまくいかないから勝手に書いたらこんな感じ。
fn new_request(line2: Box<String>) -> Box<Request> {
let len = line2.chars().count();
Box::new(Request {
line: line2,
line_len: len,
caret: 0,
})
}
あと よくわからんエラー。例えばこんなのをよく見かける。
= note: expected type `&mut std::boxed::Box<(dyn node::ResponseAccessor<_> + 'static)>`
found type `&mut std::boxed::Box<shell::Response<_>>`
dyn
とか 'static
を欲しがっているが、何をしたらいいのか分からん。
Response
が構造体としよう。 ResponseAccessor
はトレイトだぜ。
Response は ResponseAccessor だと思っていると 違うということだぜ。
impl<T> ResponseAccessor<T> for Response<T>
とか違うということだぜ。
生成の部分を書き換えてみる。
実際のコードは長ったらしいので省略してみる。
// これはダメ。
// let mut request = new_request(Box::new(line_string));
// 途中たくさん省いた。
let mut request: Box<dyn RequestAccessor> = new_request(Box::new(line_string));
自動推論に任せておけないぜ。
こんなのもある。
// まだ分かる。
fn 関数名<T> {
}
// なんだぜこれ☆?
fn 関数名<T: 'static> {
}
あと、また 分からんのが、
// これはエラーで、
pub fn do_go_btimevar(shell_var: &mut ShellVar, _request: &Box<RequestAccessor>, response:&mut Box<dyn ResponseAccessor<ShellVar>>) {
let word = &response.get_groups()[0];
let num: i32 = word.parse().unwrap();
shell_var.player_milliseconds_array[0] = num; // responseの中から飛び出た num がどこかへ行った後、
response.set_next("ND_go_wtime"); // response を変更するとエラー。
}
// これは OK☆(^~^)
pub fn do_go_btimevar(shell_var: &mut ShellVar, _request: &Box<RequestAccessor>, response:&mut Box<dyn ResponseAccessor<ShellVar>>) {
response.set_next("ND_go_wtime"); // だったら response を変更してから
let word = &response.get_groups()[0];
let num: i32 = word.parse().unwrap();
shell_var.player_milliseconds_array[0] = num; // responseの中から num は飛び出せだぜ。
}
borrow
とかいうやつだぜ。
Anyを使ったダウンキャストも分からん。
他人の記事を読めだぜ。
https://qiita.com/taskie/items/3b89962582124138a64b
std::any::Any は オブジェクト指向言語の Object型と似ているようでまた違う。
ファイルの冒頭に
use std::any::Any;
と書き、
例えば次のように trait を作るとしよう。
pub trait RequestAccessor {
fn as_mut_any(&mut self) -> &mut dyn Any;
}
struct はなんでもいいや。
pub struct Request {
}
じゃあ impl は
impl RequestAccessor for Request {
fn as_mut_any(&mut self) -> &mut dyn Any {
self
}
}
こんな感じで書く。 new も適当に書くとしよう。
fn new_request() -> Box<Request> {
Box::new(Request {
})
}
じゃあインスタンス生成は
let mut request : Box<dyn RequestAccessor> = new_request(pop_row(shell));
みたいになるわけだが、ダウンキャストは
if let Some(req) = request.as_mut_any().downcast_mut::<Request>() {
// req.value = 10;
};
みたいな感じになる。
理解できる頭の容量を超えている。
ミュータブルなダウンキャストなので .downcast_mut を使ったが、
イミュータブルなダウンキャストもある。まとめて書くと、
use std::any::Any;
pub trait ResponseAccessor<T> {
fn as_any(&self) -> &dyn Any;
}
pub struct Response<T> {
// なんか中身を書く。
}
fn new_response<T>() -> Box<Response<T>> {
Box::new(Response {
})
}
impl<T: 'static> ResponseAccessor<T> for Response<T> {
fn as_any(&self) -> &dyn Any {
self
}
}
// なんか処理。
fn nanka<T: 'static>(
response: &mut Box<dyn ResponseAccessor<T>>,
) {
let res_caret;
// downcast_mut ではなく、 downcast_ref の出番。
if let Some(res) = response.as_any().downcast_ref::<Response<T>>() {
res_caret = res.caret;
} else {
panic!("Downcast fail.");
}
// なんかセッターがあるとする。
response.set_caret(res_caret + 1);
}
てきとうに しょっぴいて並べたので 間違ってるかもしれない。
マクロ
そんなん知らね、というのに #[macro_export]
がある。
#[macro_use(hashmap)]
extern crate kifuwarabe_shell;
extern crate
の上に #[macro_use(hashmap)]
のようなものを置くのは ときどき見かけるが、
/// コピー元: https://stackoverflow.com/questions/28392008/more-concise-hashmap-initialization |More concise HashMap initialization
#[macro_export]
macro_rules! hashmap {
($( $key: expr => $val: expr ),*) => {{
let mut map = ::std::collections::HashMap::new();
$( map.insert($key, $val); )*
map
}}
}
#[macro_export]
とか探さないと見つからない。
作ったマクロは pub ではなく macro_export で公開する。
クローンと コピー
そんなん知らね。
let y = x.clone();
みたいにクローンを明示してコピーするのがクローンで、
let y = x;
みたいにクローンを明示しないコピーがコピーだなんて そんなん知らね。
use std::clone::Clone;
pub struct Node<T, S: ::std::hash::BuildHasher> {
pub token: &'static str,
// HashMapは Copy トレイトを持っていないので、Clone なら作れる。
// しかし Clone はジェネリクスに対応していないので、自作する。
// #[derive(Clone)]
pub next_link: HashMap<&'static str, &'static str, S>,
}
/// ジェネリクスに対応した clone() メソッドを作ってしまえばOK。
impl<T> Clone for Node<T> {
fn clone(&self) -> Node<T> {
self.clone()
}
}
もう何言ってるか わかんね。
move も into も分からん。
borrowed value does not live long enough
というエラーメッセージにも 泣かされている。他人の記事が参考になる。
参考にはなるが、どうすればいいのか。
「ライフタイムがコードブロックの中で尽きるのなら コピー品を作って使えばいいじゃないか」
と思ったそのコピー品も コードブロックの中で作っているので ライフタイムが尽きてしまう。
JSONファイルを読み取り、その内容を保持しておこうと
何か {
let mut file = File::open("graph.json").unwrap();
let mut data = String::new();
file.read_to_string(&mut data).unwrap();
let v: Value = serde_json::from_str(&data).unwrap();
let nodes: Vec<Value> = v["nodes"].as_array().unwrap().to_vec();
// https://docs.serde.rs/serde_json/value/enum.Value.html
for node in nodes.into_iter() { // ここで .iter() や .into_iter() でループを回しても
} // ここでライフタイムが尽きる
}
読み取ったループの中で ファイルの内容のライフタイムが尽きてしまう。
わたしは何をやっているのだろうか。
サンプルコードでは よく
println!("Please call {} at the number {}", p.name, p.phones[0]);
といったように コンソールに内容が表示されました、というところで終わってしまう。
コンソールに表示したかったわけではないんだぜ。
Rust言語では スコープの中で作ったものはスコープの中で使え、といった癖のようなものがあって
つまづいてしまう。
だったら ファイルの読み取りは トップレベルのスコープで しろというのか☆?(^q^)
例えば
pub fn insert_node(
&mut self,
name: &'static str, // この型が悪いのか?
) {
self.node_table.insert(
name.to_string(),
Node {
},
);
}
&'static str
なんか引数にしてるから悪いとかあるのだろうか?
&String
の方が良かったりするのだろうか? さっぱり分からん。
String
想像: ヒープ上の[あ][い][う][え][お]。UTF-8 で入っている。正体は 8byte要素のVec。
&String
想像: ヒープ上の[あ]のアドレス
str
想像: スタック上の[あ][い][う][え][お]。UTF-8 で入っている。正体は 8byte要素の配列。
&'static str
想像: スタック上の[あ]のアドレス
&str
想像: &'static str と同じ。
かと思うんだが違うのか。少なくとも 文字列 の利用方法に 明確に型を合わせることができないと
エラーが出てくる。
- struct はメモリ構成を定義するものだから、ライフタイムが不定のものを入れるのは避けメンバーの型には &Struct ではなく String にしたり。
例えば
fn aaaa() -> String {
"aaaa"
}
関数の返り値に String 型を書くことはできるが、いつも "aaaa" を返すようなときだぜ。いずれ
fn aaaa() -> &String {
&"aaaa"
}
いろんな文字列を返したくなって 頭に & を付けるときが来る。そして 寿命が短いとか言われるんだぜ。
だったら
fn aaaa(name:&String) -> &String {
name
}
関数の中で文字列を新しく作るのはやめて、引数で取ってきて、返り値で返す、という
構文が頭に できあがってくると 関数プログラミングっぽくなってくるが、まだ先だぜ。
HashMap の ,S も分からん。
note: expected type `&mut std::collections::HashMap<std::string::String, std::vec::Vec<std::string::String>>`
found type `&mut std::collections::HashMap<std::string::String, std::vec::Vec<std::string::String>, S>`
なんかハッシュビルダーの S を付けろというので付けたら付けたで
ハッシュビルダーの S が付いているからエラーにされる。どうしろというのか。分からん。
// let mut entrance_map : HashMap<String, Vec<String>> = [].iter().cloned().collect();
// let mut entrance_map : HashMap<String, Vec<String>, S> = [].iter().cloned().collect();
// let mut entrance_map : HashMap<String, Vec<String>> = HashMap::new();
let mut entrance_map : HashMap<String, Vec<String>, S> = HashMap::new();
他にどういう書きようがあるのか。
誰が読んでるか知らないが
Rust 言語の記事は他にも書いている☆(^~^)