AWS Lambda で Rustを使ってみた話

この記事は、nacika.com Rust記事 アドベントカレンダー Advent Calendar 2018 – Adventar 二日目の記事です。

AWS LambdaでRustを使う方法はほぼ、<a href=”https://dev.classmethod.jp/cloud/aws/custom-runtime-rust/” title=”AWS LambdaのCustom RuntimeでRustを実行してみた #reinvent | DevelopersIO target=”_blank”>AWS LambdaのCustom RuntimeでRustを実行してみた #reinvent | DevelopersIO こちらや Rust Runtime for AWS Lambda | AWS Open Source Blog こちらの記事の通りに作ると簡単につくれます。

手順はもうあるので、作ってみた感想などを書いていこうと思います。

ランタイム is 何処

ランタイムは、AWSが用意しているというよりはcreateとして提供されているようです。
Cargo.toml にある lambda_runtime = "^0.1" というのがそれですね。

main.rsの編集だけで終わる

びっくりしたのが、main.rsに一通りの処理を書くだけですぐに使える所。内部的にはLambda用のリクエストを受け付ける処理があるようですが全てランタイムがやってくれます。便利。
そのため、こちらでやることはハンドラを書くことくらい。

必要なイベントを型で受け取る

#[derive(Deserialize, Clone)]
struct CustomEvent { #[serde(rename = "text")] text: String, #[serde(rename = "is_decode")] is_decode: bool
}

serde(rename = "text") が実際のイベント名、下にあるのが型のフィールド。
イベントがそのまま型で来るのでそこから取ってくればOK。falseやtrueがboolで処理してくれるのも地味に嬉しい。(オプション型は試せていない)

気をつける所

Cargo.tomlの

[[bin]]
name = "bootstrap"

この部分、buildされたバイナリのファイル名がbootstrapじゃないとエラーになる。

実行時間

最安プランになっている様子。これ以上高速化しても安くならない・・?

この開発によって作ったもの

モコあきさんが2001年にリリースしたWindowsアプリのロリコンバータをリメイクしたものを作りました
https://nacika.com/loliconv/

カテゴリー: Rust

actixを試す

この記事は nacika.com Rust記事 アドベントカレンダー Advent Calendar 2018 – Adventar の1日目の記事です。
日付跨いじゃってるけど・・・。

早速、actix/actix: Actor framework for Rust を試しました。

nacika-ins/actix-sample

使った感じ

現状だと、特に各アクターでスレッドを作ってくれるわけではないようで、どこかのアクターでloopを書いたりしてスレッドを専有してしまうと、他のアクターの処理が止まる。
panic!を書けばプロセスは落ちるし、エラーが発生してもプロセスごと落ちる。

impl Superviseddef restarting というメソッドがあり、エラーがあると呼ばれてリスタートするらしいんだけど、呼ばれない。。

メリットがありそうなところ

  • 耐障害性は、スレッドを作ってあげて処理すればなんとかなりそう。do_sendや、sendなどのメソッドは #[derive(Message)] をつけたものを送ればいいだけなので、複雑な所有権をあまり考えずに使えそうなところがよさそう。
  • スレッドループからもメッセージを送ることができるのでいい感じ。
  • アクターの変数も、メッセージ自体もミュータブルにすることができる。

今後使うか

正直、Rust言語の複雑な部分をActorでまとめることができてコード自体も簡潔にかける部分が良いので、今後使っていきたい。

カテゴリー: Rust

スレッドに名前をつける

スレッドがコケたときに、スレッドの名前が表示される。エラー時の原因特定に便利。

use std::thread;
thread::Builder::new() .name("thread name".to_string()) .spawn(move || { println!("Hello, world!"); });
カテゴリー: Rust

flat_map で Vec<String> を得る

flat_map だと、 cloned() しないと、 Option<&String> になってエラーになる。
Vec<String> として取り出したい場合は、 cloned() をつける。

let result: Vec<String> = vec![Some("hoge".to_owned()), None] .iter() .flat_map( |f| f ) .cloned() .collect();
println!("{:?}", result);
カテゴリー: Rust

Rust Option入門者向けの補足追記

Option

https://doc.rust-lang.org/std/option/enum.Option.html の補足追記です。

この記事は MIT ライセンスです。

is_some

Someならtrue

let x: Option<u32> = Some(2);
assert_eq!(x.is_some(), true);
let x: Option<u32> = None;
assert_eq!(x.is_some(), false);

is_none

Noneならtrue

let x: Option<u32> = Some(2);
assert_eq!(x.is_none(), false);
let x: Option<u32> = None;
assert_eq!(x.is_none(), true);

as_ref

Option を Option<&T> に変換する。この手法によって、元の値には触れず、参照のみを受け取ります。
borrow は発生しません。

let num_as_str: Option<String> = Some("10".to_string());
// First, cast `Option<String>` to `Option<&String>` with `as_ref`,
// then consume *that* with `map`, leaving `num_as_str` on the stack.
let num_as_int: Option<usize> = num_as_str.as_ref().map(|n| n.len());
println!("still can print num_as_str: {:?}", num_as_str);

as_mut

Option を Option<&mut T> に変換

let mut x = Some(2);
match x.as_mut() { Some(v) => *v = 42, None => {},
}
assert_eq!(x, Some(42));

expect

オプションをアンラップし、Someの中身を返す。
unwrapと異なる点は、panicが発生した場合に、msgが流れる

let x = Some("value");
assert_eq!(x.expect("the world is ending"), "value");

unwrap

Some(v) から vを取り出す。
もとの変数からは移動されてしまうので注意。
Noneだった場合はpanicが発生する

let x: Option<&str> = None;
x.expect("the world is ending"); // panics with `the world is ending`

unwrap_or

アンラップ時に、Noneだった場合は、orで指定したものを返す。制約として同じ型のものでなければいけない。

assert_eq!(Some("car").unwrap_or("bike"), "car");
assert_eq!(None.unwrap_or("bike"), "bike");

unwrap_or_else

アンラップ時にNoneだった場合は、or_elseで指定したクロージャの結果を返す。

let k = 10;
assert_eq!(Some(4).unwrap_or_else(|| 2 * k), 4);
assert_eq!(None.unwrap_or_else(|| 2 * k), 20);

map

アンラップ時にSomeだった場合は、Some(v)のvをクロージャに渡し、結果をSome(s)として受け取る。Noneだった場合はそのままNoneになる。

map_or

アンラップ時にSomeだった場合は、Some(v)のvをクロージャに渡し、結果をsとして受け取る。Noneだった場合はデフォルト値が渡される。結果sとデフォルト値は同じ型。

let x = Some("foo");
assert_eq!(x.map_or(42, |v| v.len()), 3);
let x: Option<&str> = None;
assert_eq!(x.map_or(42, |v| v.len()), 42);

map_or_else

Someだった場合もNoneだった場合もクロージャに渡す。結果の型はそれぞれのクロージャで同じでなければいけない。

let k = 21;
let x = Some("foo");
assert_eq!(x.map_or_else(|| 2 * k, |v| v.len()), 3);
let x: Option<&str> = None;
assert_eq!(x.map_or_else(|| 2 * k, |v| v.len()), 42);

ok_or

Option型をResult型に変換する。失敗だった場合の処理は新たに追加する。

let x = Some("foo");
assert_eq!(x.ok_or(0), Ok("foo"));
let x: Option<&str> = None;
assert_eq!(x.ok_or(0), Err(0));

or_or_else

Option型をResult型に変換する。失敗だった場合の処理は、クロージャで計算する。

let x = Some("foo");
assert_eq!(x.ok_or_else(|| 0), Ok("foo"));
let x: Option<&str> = None;
assert_eq!(x.ok_or_else(|| 0), Err(0));

iter

イテレータに変換する。next()すると結果が変える。
二回next()するとNoneになる。

let x = Some(4);
assert_eq!(x.iter().next(), Some(&4));
let x: Option<u32> = None;
assert_eq!(x.iter().next(), None);

iter_mut

iterと一緒だが、ミュータブル

let mut x = Some(4);
match x.iter_mut().next() { Some(v) => *v = 42, None => {},
}
assert_eq!(x, Some(42));
let mut x: Option<u32> = None;
assert_eq!(x.iter_mut().next(), None);

and

引数の条件もSomeでなければ、全体的にNoneになる。

let x = Some(2);
let y: Option<&str> = None;
assert_eq!(x.and(y), None);
let x: Option<u32> = None;
let y = Some("foo");
assert_eq!(x.and(y), None);
let x = Some(2);
let y = Some("foo");
assert_eq!(x.and(y), Some("foo"));
let x: Option<u32> = None;
let y: Option<&str> = None;
assert_eq!(x.and(y), None);

and_then

NoneのときはNoneを返すが、Someだった場合は、クロージャの計算結果を Some<U> として返す。

fn sq(x: u32) -> Option<u32> { Some(x * x) }
fn nope(_: u32) -> Option<u32> { None }
assert_eq!(Some(2).and_then(sq).and_then(sq), Some(16));
assert_eq!(Some(2).and_then(sq).and_then(nope), None);
assert_eq!(Some(2).and_then(nope).and_then(sq), None);
assert_eq!(None.and_then(sq).and_then(sq), None);

or

どちらかに値が入っていれば、最初にHITした値が帰る

let x = Some(2);
let y = None;
assert_eq!(x.or(y), Some(2));
let x = None;
let y = Some(100);
assert_eq!(x.or(y), Some(100));
let x = Some(2);
let y = Some(100);
assert_eq!(x.or(y), Some(2));
let x: Option<u32> = None;
let y = None;
assert_eq!(x.or(y), None);

or_else

値が含まれていればSomeを返すが、含まれていない場合は、クロージャが呼ばれその結果を返す。クロージャの結果はSomeかNoneである。

fn nobody() -> Option<&'static str> { None }
fn vikings() -> Option<&'static str> { Some("vikings") }
assert_eq!(Some("barbarians").or_else(vikings), Some("barbarians"));
assert_eq!(None.or_else(vikings), Some("vikings"));
assert_eq!(None.or_else(nobody), None);

get_or_insert

Nonen場合は、vをオプションに挿入し、含まれている値への変更可能な参照を返す。

let mut x = None;
{ let y: &mut u32 = x.get_or_insert(5); assert_eq!(y, &5); *y = 7;
}
assert_eq!(x, Some(7));

get_or_insert_with

Noneの場合、クロージャによって計算された値をオプションに挿入し、含まれている値への変更可能な参照を返す。

let mut x = None;
{ let y: &mut u32 = x.get_or_insert_with(|| 5); assert_eq!(y, &5); *y = 7;
}
assert_eq!(x, Some(7));

take

オプションから値を取り除き、Noneをその場所に残す。

let mut x = Some(2);
x.take();
assert_eq!(x, None);
let mut x: Option<u32> = None;
x.take();
assert_eq!(x, None);
カテゴリー: Rust

cargoでインストールしたcrateのバージョンが知りたい

今だとあんまりないですが、よく Cargo.tomlのバージョン指定で、

env_logger = "*"

みたいなのがありました。
でもバージョンを固定したくなったときに、実際のバージョンがわかりません。
こんな時に、現在のバージョンや、最新のバージョンなどを一覧で出してくれる便利なツールがあります。

cargo-outdated

cargo-outdatedは、Cargo.tomlにある依存関係の他に、ライブラリが更に依存している依存関係まで全て調べ上げてくれます。将来的なバージョンが削除されているようなことも全てわかるので、今後のアップデートの参考になります。

こういう出力例になります。

Name Project Compat Latest Kind Platform
---- ------- ------ ------ ---- --------
Inflector->lazy_static 0.2.8 0.2.9 0.2.9 Normal ---
Inflector->regex 0.2.1 0.2.2 0.2.2 Normal ---
aho-corasick->memchr 1.0.1 1.0.2 1.0.2 Normal ---
bytes 0.4.2 0.4.5 0.4.5 Normal ---
bytes->byteorder 1.0.0 1.1.0 1.1.0 Normal ---
bytes->iovec 0.1.0 0.1.1 0.1.1 Normal ---
カテゴリー: Rust

Rust で ファイルダウンロード

そんなに難しいわけではないです。

[dependencies]
hyper = "^0.7"
// hyper
extern crate hyper;
use std::io::Read;
use hyper::Client;
use hyper::client::{Request, Response};
use hyper::header::{Connection, Headers, UserAgent};
fn main () { let mut client = Client::new(); file_download(&client, "http://");
}
fn file_download<'a>(client: &'a Client, url: &str) { let mut oRes = client.get(url) .header(Connection::close()) .header(UserAgent("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_3) \ AppleWebKit/537.36 (KHTML, like Gecko) \ Chrome/48.0.2564.97 Safari/537.36" .to_string())) .send(); let mut res = oRes.unwrap(); println!("{:?}", res); let mut body: Vec<u8> = vec![]; res.read_to_end(&mut body).unwrap(); let mut f = File::create("foo.jpg").unwrap(); println!("{:?}", f); f.write_all(&body);
}
カテゴリー: Rust

Rust で Webスクレイピング 途中経過

毎日ぼちぼちとRustでWebスクレイピングするライブラリを作っています。
と言ってもServoが開発している、html5ever のラッパーなんですけどね。

前回の記事で作っていたものがよく見てみたら、getElementByIdGetElementsByTagNameが、子孫ノードを持っていなくて、ちょっと改修しました。html5ever は NodeNodeEnumCrone Traitを実装しておらず、cloneができなくて困っていたんですが、どうやらHandleはcloneができることに気づいた。それを利用して、HandleをVecに追加する際にHandleごとCloneする方法を取りました。

とりあえずこの二つさえあればなんとかスクレイピングできそうですが、jQueryに寄せたいとも思っているので 少しだけでもjQueryライクにできればなと思ってます。

カテゴリー: Rust

[Rust] スライスせずに配列から値を取り出す

またRustについてです。Vec型から値を取り出そうと試行錯誤していたんですが、どうもうまくいきませんでした。

let mut a: String = vec![];
a.push("hoge".to_owned());
a.get(0) // <--- ここでスライスされてしまう

こうしてしまうと、a.get(0) で取り出した値は、aのスライスとなるため Some(&String) この型が返ってしまいます。

答えを言うと、

a.pop();

でよかったです。

カテゴリー: Rust