RustのOption型をmapとand_thenで上手に取り出す

以前matchについて書きました。今回はmapand_thenというコンビネータを利用した値の取り出しについて書いてみます。

map

mapは値を加工できるが、型自体を変更できない。SomeはSome、NoneはNoneになる。

fn main() {

    let case1: Option<i8> = Some(1);
    let case2: Option<i8> = None;

    assert_eq!(case1.map(|n| n * 2 ), Some(2));
    assert_eq!(case2.map(|n| n * 2 ), None);
}

and_then

and_then は返す型を変更できる。

fn main() {

    let case1: Option<i8> = Some(1);
    let case2: Option<i8> = Some(2);

    assert_eq!(case1.and_then(|n| if n == 2 { Some(n) } else { None }), None);
    assert_eq!(case2.and_then(|n| if n == 2 { Some(n) } else { None }), Some(2));
}

Vimでリモートホストのファイルを編集する

Vimでファイルを編集して保存するとリモートにファイルが作成されました。直接リモートに入ってviで開くとその環境のvimrcなどが使われますが、この方式を使えば手元のvimの設定でファイルを編集することができます。

vim scp://user@remote//path/to/hoge.text

https://orebibou.com/2015/02/vim%E3%81%A7%E3%83%AA%E3%83%A2%E3%83%BC%E3%83%88%E5%85%88%E3%81%AE%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%92ssh%E3%83%97%E3%83%AD%E3%83%88%E3%82%B3%E3%83%AB%E7%B5%8C%E7%94%B1%E3%81%A7%E7%B7%A8/

RustのVec<Result<_>>をResult<Vec<_>>に変換する

Resultを持つ配列で、Okなものを取り出すのにfilterしていて面倒だなと思ったら、どうやらVec<Result<_>>Result<Vec<_>>に変換することができるんですって。これをするとOkだけが含まれている場合はOkだけを返し、Errが一つでも含まれているとErrを返してくれるので、より実用に沿ったコードが書ける。

fn main() {
    assert_eq!([Ok(1), Ok(2), Ok(3)].iter().cloned().collect::<Result<Vec<i8>, u8>>(), Ok(vec![1, 2, 3]));
    assert_eq!([Err(1), Err(2), Ok(3)].iter().cloned().collect::<Result<Vec<i8>, u8>>(), Err(1));
}

なぜこれができるのか。細かな応用の説明は下記が参考になりました。

https://qnighy.hatenablog.com/entry/2017/06/14/220000

RustでOptionをResultにok_orで変換する

自分で判定したりmapを使ってResultにする方法ありますが、一番シンプルに書けるのはok_orを使う方法だと思いました。

fn main() {
    let hoge1: Option<i8> = Some(1);
    let hoge2: Option<i8> = None;

    assert_eq!(hoge1.ok_or("test"), Ok(1));
    assert_eq!(hoge2.ok_or("test"), Err("test"));
}

ok_orを通すことでSomeOkになり、NoneErrになっており、値が中に与えたものになっていることがわかります。

RustのOption型をmatchで上手に取り出す

matchOption型を渡すとunwrapしてくれるのでコード量が若干減ります

fn main() {
    let hoge1: Option<i8> = Some(1);
    let hoge2: Option<i8> = None;

    assert_eq!(1 as i8, match hoge1 { Some(x) => x, _ => 8 as i8 });
    assert_eq!(8 as i8, match hoge2 { Some(x) => x, _ => 8 as i8 });
}

matchを使わないで処理を書く場合ifを使うことになりますが、そんなにコード量が増えるわけではないですが、前回の記事に書いたようにunwrapはパニックを起こすものなので、matchを使い、unwrapがコード上に現れないようにするほうが良さそうです。

fn main() {
    let hoge1: Option<i8> = Some(1);
    let hoge2: Option<i8> = None;

    let res1: i8 = if hoge1.is_some() { hoge1.unwrap() } else { 8 };
    let res2: i8 = if hoge2.is_some() { hoge1.unwrap() } else { 8 };

    assert_eq!(1, res1);
    assert_eq!(8, res2);
}

他にmap系を使う方法もあります。これらはResultの時と似たようなもので、今度説明しますね。

RustのResult型に対してmapとmap_errで値を加工する

特定処理の結果Resultを受け取るパターンにおいて、最終的に返すべき値とは違うResultだった場合にis_okなどを使ってif分岐で新たなResultを定義してもよいが、mapmap_errを使えば簡単に加工できる。

fn stringify(e: i8) -> String { format!("error: {}", e) }

fn main() {
    let a: Result<i8, i8> = Ok(10);
    let b: Result<i8, i8> = Err(20);

    assert_eq!(a.map(|ok| { ok * 2 }).map_err(|e| { format!("error: {}", e) }), Ok(20));
    assert_eq!(b.map(|ok| { ok * 2 }).map_err(|e| { format!("error: {}", e) }), Err("error: 20".to_string()));

    assert_eq!(a.map(|ok| { ok * 2 }).map_err(stringify), Ok(20));
    assert_eq!(b.map(|ok| { ok * 2 }).map_err(stringify), Err("error: 20".to_string()));
}

同じエラーの処理をするなら関数でフォーマットを渡すのも手。

https://doc.rust-lang.org/std/result/enum.Result.html

RustでOption型からunwrap_or系で値を取り出しパニックを避ける

Option型はunwrap()で中の値を取り出せますが、中身がNoneの場合は下記のようにパニックになってしまいます。

thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', src/libcore/option.rs:378:21

unwrap_or

値がNoneの時に確定している値を返す場合は

fn main() {
    let hoge1: Option<&str> = Some("test");
    let hoge2: Option<&str> = None;

    assert_eq!(hoge1.unwrap_or("gueee"), "test");
    assert_eq!(hoge2.unwrap_or("gueee"), "gueee");
}

unwrap_or_else

値がNoneの時に処理を実行したい場合はunwrap_or_elseを使うのが良い

fn main() {
    let default = 2;
    let hoge1: Option<i8> = Some(1);
    let hoge2: Option<i8> = None;

    assert_eq!(hoge1.unwrap_or_else(||{default * 2}), 1);
    assert_eq!(hoge2.unwrap_or_else(||{default * 2}), 4);
}

WordPressにアップロードした画像のEXIF情報を削除するプラグインEWWW Image Optimize

screenshot

https://ja.wordpress.org/plugins/ewww-image-optimizer

JPEG画像にはEXIF情報というものがあり、位置情報などが含まれている場合があります。そのままファイルをアップロードしてしまうと自分の家の住所や現在どこにいるかなどが第三者に分かってしまいます。

通常WordPressやインターネット上に画像ファイルをアップロードする場合、この位置情報などを除去してからアップロードするかと思いますが、わざわざローカルでこれを除去してからアップロードとなると面倒です。

EWWW Image Optimizeは、WordPressにファイルアップロード時にこのメタデータを除去してくれるため、ローカルでの作業が不要になり効率的です。EWWW Image OptimizeはEXIF情報を削除するだけではなく、ファイルの圧縮やJPEGからPNGへの変換なども行ってくれる他、大きすぎる画像については最大値を設定してそのサイズまで画質を落としてくれます。