Open6

Rustメモ

乳牛乳牛

拡張メソッド

ライブラリ等で提供されているTraitのソースを変更すること無くメソッドを追加する。
C#の拡張メソッドっぽいやつ。

参考になるCrate
https://github.com/BurntSushi/byteorder

例えばReadに下記のようなメソッドを生やす。

use std::io::Cursor;
use byteorder::ReadBytesExt;

let mut rdr = Cursor::new(vec![2, 5]);
assert_eq!(2, rdr.read_u8().unwrap());
assert_eq!(5, rdr.read_u8().unwrap());

実装

pub trait ReadBytesExt: io::Read {
    fn read_u8(&mut self) -> Result<u8> {
        let mut buf = [0; 1];
        self.read_exact(&mut buf)?;
        Ok(buf[0])
    }
}

impl<R: io::Read + ?Sized> ReadBytesExt for R {}

全文はこちら

  • io::ReadのSub Traitとして定義することでio::Readのメソッドに依存できる
  • Traitにデフォルト実装をしている
  • R: io::Read に implすることでメソッドを付け足す
    • Traitに対してのimplなのでTrait境界の書き方になる
乳牛乳牛

しかしデフォルト実装に頼りまくるのはなんとなくtraitのコンセプトに反しているような気持ち悪さを感じる。付け足す拡張メソッド数が増えると楽するためにデフォルト実装使いたくなる気持ちはわかる。
https://twitter.com/NewGyu/status/1608738146652684290

乳牛乳牛

Option::mapのラムダがResultを返す場合

Result<Option<i32>> で返したい。

        #[test]
        fn test() {
            use anyhow::*;
            let v = Some("123");
            let x = match v {
                Some(str) => str
                    .parse::<i32>()
                    .context("failed to convert to i32")
                    .map(Some),
                None => Ok(None),
            };
            assert_eq!(x.unwrap(), Some(123));
        }

長いのでこう書ける。

        #[test]
        fn test2() {
            use anyhow::*;
            let v = Some("123");
            let x = v
                .map(|str| str.parse::<i32>().context("failed to convert to i32"))
                .transpose();
            assert_eq!(x.unwrap(), Some(123));
        }

https://doc.rust-jp.rs/rust-by-example-ja/error/multiple_error_types/option_result.html
https://doc.rust-lang.org/stable/std/option/enum.Option.html#method.transpose