33
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

RustのtraitとかimplとかBox<dyn trait>とか<T>のジェネリクスとかAnyとかmacro_exportとかOptionとかResultとかmutとかunwrapとかexpectとか分からん☆(^q^)

Last updated at Posted at 2018-09-20

だらだら書く。この記事は 誰も読んでないので たまにこっそり書き換える。

説明書も読まず 適当に書いている。わたしには 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 みたいなもんかと思ってしまうが違う。

implimpl トレイト名 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 言語の記事は他にも書いている☆(^~^)

https://qiita.com/muzudho1/items/26246ea3f0ed51c0bb10

https://qiita.com/muzudho1/items/1db874028fd1b0dae418

33
13
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
33
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?