ごらくらいふ

プログラミングしたりゲームしたり

Zaif-api-rust v0.5.0をリリースした。trait From、エラーハンドリングに便利だった。

ついさっき、Rustで書いたzaif-apiのwrapper、v0.5.0をリリースした。

github.com

今回の改修はエラーハンドリングが目的で、「まず動く」で作っていたところを取り回しできるようにした。

活躍したのが標準ライブラリに入っているFrom<T>トレイトで、あるstructがこれを実装するということは、特定の型から変換できると宣言することになる。

fn dl_and_parse (loader: http::Downloader, parser: parse::Parser) -> Result<Parsed, parse::Error> {
    // ダウンロード処理
    let text = downloader.fetch().unwrap().text().unwrap();
    // パースして返す
    parser.from_text(text)
}

まず動くものとしてこういうものを書いた。 これだと、downloader.fetch()で問題があった場合などにpanicを起こしてしまう。

落ちないようにErrを返してあげたいが、httpcrateがparseのErrorを持っていないので、単純にunwrapを?に書き換えただけではコンパイルエラーになってしまう。

これを解決するのが、Fromトレイト。 これを実装することで異なる型に馴染むことができる。

とはいえ、よそのcrateのErrorをまた別のcrateのErrorに差し込むのは流石に行儀が悪いので、Enumを用意して各crateのerrorを包むだけにした。 (それに、自分で丁寧なエラーハンドリングをしなくて良いので。)

enum MyError {
    HttpError(http::Error),
    ParseError(parse::Error),
}

impl From<http::Error> for Error {
    fn from(err: http::Error) -> Self {
        Error::HttpError(err)
    }
}
impl From<parse::Error> for Error {
    fn from(err: parse::Error) -> Self {
        Error::ParserError(err)
    }
}

これにより、unwrap()を排除しつつ、panicを起こさずに異常を伝えられるようになった。

fn dl_and_parse (loader: http::Downloader, parser: parse::Parser) -> Result<Parsed, MyError> {
    // ダウンロード処理
    let text = downloader.fetch()?.text()?;
    // パースして返す
    Ok(parser.from_text(text)?)
}

参考リンク

RustでOption値やResult値を上手に扱う - Qiita : https://qiita.com/tatsuya6502/items/cd41599291e2e5f38a4a

DLLってなんなのん

DLLとは

ダイナミックリンクライブラリと読む。 動的なリンクで利用されるライブラリ。

対義語: 静的リンクライブラリの認識

リンクというものは、以下の認識でいる。

  • プログラムは、複数のコンパイルされたマシン語のプログラム片(以下、プログラム片)から成る。
  • ひとつのソースコードコンパイルするとひとつのプログラム片が生成される。
  • このプログラム片には、「処理」「処理の開始点」「処理の終了処理(return等)」が含まれている
  • リンカーが、それぞれのプログラム片の「処理」と「処理の開始点」をつなぎこむ

中でも静的リンクライブラリは、プログラムの中に直接つなぎこまれるモノを言う認識。 それ故にプログラムは単体で完結できるが、複数のプログラムで、まったく同じ処理が存在することがある。 これがリソースの無駄遣いに繋がる。

動的リンク

解決する要素

https://support.microsoft.com/ja-jp/help/815065/what-is-a-dll より

コードのモジュール化、コードの再利用、メモリ使用の効率化、ディスク領域の削減が促進されます。 これにより、オペレーティング システムおよびプログラムの読み込みや実行が速くなり、コンピュータ内で占めるディスク領域が小さくなります。

また、以下のメリットが挙げられている。

  • 使用するリソースが少ない
  • モジュラー アーキテクチャが促進される
  • 配置およびインストールが容易になる

動的リンクと静的リンクの違い

「処理」と「処理の開始点」のつなぎ込みで、以下の違いが現れる。

あるプログラム片の処理から、あるプログラム片の処理の開始点へ移動する際

  • メモリ上のどの地点へ移動すればよいか、プログラム内で予め判明しているものが静的リンク
  • メモリ上のどの地点へ移動すればよいか、システムに問い合わせて判明するものが動的リンク

所感

動的リンクの利点はわかったけれど、結局アプリケーションを配布する時にDLL同梱してない? リソースが少ない利点を享受しているの、.NET Framworkくらいなのでは…

参考リンク

Rustの話: traitの定義部分では、フィールドに干渉できない

前提

rustc 1.22.1

本題

traitを書く時、こういう書き方はできない。

trait Horizontal {
    // x:i64 というフィールドの存在を前提とする
    // x をカウントアップして、新しい値を返す
    fn count_up (&mut self) -> i64 {
        self.x += 1;
        self.x
    }
}

struct Point {
    x: i64, // コンパイラにフィールドの追加を強制される
}
impl Horizontal for Point {}

前提としてtraitのデフォルト実装はフィールドに干渉できない。

Traitを実装したStructに特定のフィールドを強制したい場合、 例えば、getter/setterの定義を用意することで実現できる。

trait Horizontal {
    fn get_x (&self) -> i64;
    fn set_x (&mut self, val: i64);
    
    fn count_up (&mut self) {
        let next = self.get_x() + 1;
        self.set_x( next );
        self.get_x()
    }
}

struct Point {
    x: i64, // getter/setterのためにフィールドを用意する
}
impl Horizontal for Point {
    fn get_x (&self) -> i64 {
        self.x
    }
    fn set_x (&mut self, val: i64) {
        self.x = val;
    }
}

余談: getter/setterのマクロを書いてみた

traitで使うaccessorと、実装時に使うaccessor_impl

macro_rules! accessor {
    ((get=$getter:ident) : $type:ty ) => {
        fn $getter(&self) -> $type ;
    };
    ((set=$setter:ident) : $type:ty ) => {
        fn $setter(&mut self, value:$type) ;
    };
    ((get=$getter:ident, set=$setter:ident) : $type:ty ) => {
        accessor!((get=$getter): $type);
        accessor!((set=$setter): $type);
    };
}
macro_rules! accessor_impl {
    ((get=$getter:ident) $name:ident : $type:ty ) => {
        fn $getter(&self) -> $type {
            self.$name
        }
    };
    ((set=$setter:ident) $name:ident : $type:ty ) => {
        fn $setter(&mut self, value: $type) {
            self.$name = value;
        }
    };
    ((get=$getter:ident, set=$setter:ident) $name:ident : $type:ty ) => {
        accessor_impl!((get=$getter) $name:$type);
        accessor_impl!((set=$setter) $name:$type);
    };
}

このマクロを使うとこんな感じになる。フィールドが増えないと効き目が薄い。

trait Horizontal {
    accessor!((get = get_x, set = set_x): i64);
    fn count_up(&mut self) -> i64 {
        let next = self.get_x() + 1;
        self.set_x(next);
        self.get_x()
    }
}
struct Point {
    x: i64,
}

impl Horizontal for Point {
    accessor_impl!((get = get_x, set = set_x) x: i64);
}

Grepのオプション no-filename(-h) を使う時

grepコマンドのオプションに--no-filename(-h)というものがある。 これは、マッチした内容をファイル名、Pathのプレフィックスを除いて結果を出力してくれる。

単一ファイルの検索や、標準入力からの検索時には、デフォルトで有効となっている。

用途

複数のファイルから検索して、かつプレフィックスが邪魔になるケース。

たとえばObjective-Cで、import文を調査する時に活用できる。

grep "#import" -h -R src | sort | uniq

Zaif Apiに対する知見

Zaif Api v1.1.1 http://techbureau-api-document.readthedocs.io/ja/latest/index.html

Zaif APIを弄ってて得た覚え書き。

  • trade_api/active_ordersや、trade_api/cancel_ordercurrency_pairは任意項目となっているが、入力しないと挙動が安定しない。
  • ほぼ開発時のデバッグにしか影響しないが、trade_api/tradeの結果で得た注文のcancel_orderは、少なくとも5秒待てば実行可能だった。

Rustでzaif apiのwrapperを書いている

Rustでzaif apiのwrappreを書いている。

reqwestでもりもり実装していて、なんか楽しい。

まだ借用チェッカーと戦ってるボーイなので、もりもりコピーして構造体たち皆所有権のあるデータ持つ、みたいな書き方してる。 まぁ使う分には引数に与えたデータを延命させたりとか、そういう気を使わなくて良いんじゃないでしょうか。教えてくれ。

Crate

https://crates.io/crates/zaif-api

github.com

すげぇ

cargo publishするとcrate.ioに載って、Cargo.tomldependenciesに依存対象として書けるようになる。

驚いたのはDocs.rsに自動でドキュメントが出力されること。かしこい~~。

列挙型で error[E0507]: cannot move out of borrowed content に対処した

ムーブセマンティクスを忘れていた話。

Builderパターンで以下のように書いたところコンパイラerror[E0507]: cannot move out of borrowed contentと怒られた。

enum Method {
    Get,
    Post,
}
struct Client {
    method: Method,
}
struct ClientBuilder {
    method: Method,
}
impl ClientBuilder {
    fn new() -> Self {
        ClientBuilder {
            method: Method::Get,
        }
    }
    fn method(&mut self, method: Method) -> &mut Self {
        self.method = method;
        self
    }
    fn finalize(&self) -> Client {
        Client {
            method: self.method,
        }
    }
}

ClientBuilder.finalize()で、self.methodの所有権を失ってしまうために怒られていた。

対応

列挙型MethodCopyトレイトを実装することで解決できる。そのためのアトリビュートもある。

#[derive(Copy, Clone)]
enum Method {
    Get,
    Post,
}

認識の修正

この対処方法はドキュメントにも書いてある

このケースは、以下の認識で書いたために引っかかってしまった。

  • enumの中身は数値だろう、すなわち、プリミティブな値だろう
  • プリミティブな値であればコピーされて所有権を失わないだろう

この認識を改めていく。

enumの中身はプリミティブな値ではない

ドキュメント 列挙型 より

列挙型の値(ヴァリアントと呼ぶ)は構造体の定義である。 Unit-like構造体であったり、名前付きフィールドを持つ構造体であったり、タプル構造体であったり。

enum Message {
    Quit,
    ChangeColor(i32, i32, i32),
    Move { x: i32, y: i32 },
    Write(String),
}

そもそもプリミティブな値だから所有権を失わないのではない

ドキュメント 所有権 より

全てのプリミティブ型は Copy トレイトを実装しているので、推測どおりそれらの所有権は「所有権ルール」に従ってはムーブしません。

Copyトレイトを実装しているから、所有権を失わない。

所感

見事にドキュメントで説明される事柄を踏み抜いた