RustでAPIサーバを

作り始めた話

Shinjuku.rs #3 @FORCIA

2019/3/12

ゆで卵

自己紹介

  • 名前: ゆで卵
  • Twitter: @takayukib
  • Github: yudetamago
  • フリーランスのブロックチェーンエンジニア
  • TokenPocket Inc., LayerX Inc. 

経緯と今日の内容

  1. 某バックエンド(APIサーバ+DB)を作ることになった
  2. どうせ自分しかメンテしないしRustで作ろう
  3. とりあえず使うライブラリは決めた
  4. なんか色々分からなかったりしたのでライブラリのコード読んでる←イマココ

WAFどれ使う

  • 活発に開発されている
  • 低レイヤーすぎない (✗ hyper)
  • 情報源多いほうが良い 
  • Rust stable versionで使える (✗ Rocket)
  • ORMやConnection Poolと連携しやすい
  • (出来れば非同期に対応してるやつ)
  • (出来ればフルスタックすぎないやつ)
  • (出来れば枯れてるやつがいいけどたぶんそんなものは無い)

使うやつ

  • WAF: actix_web
  • ORM: diesel
  • Connection Pool: r2d2

参考文献

迷ったところ・

はまったところの

軽いコードリーディング

// Option 1:  passed as a parameter to a handler function
fn index((params, info): (Path<(String, String,)>, Json<MyInfo>)) -> HttpResponse {
   ... 
}


// Option 2:  accessed by calling extract() on the Extractor

use actix_web::FromRequest;

fn index(req: &HttpRequest) -> HttpResponse {
	let params = Path::<(String, String)>::extract(req);
	let info = Json::<MyInfo>::extract(req); 

	...
}

https://actix.rs/docs/extractors/

途中に型?🤔

// Expression paths ///////////////////////////////////////////////////////////////

a::b::c         // reference to a function `c` in module `a::b`
a::<T1, T2>     // the function `a` instantiated with type arguments `T1`, `T2`
Vec::<T>::new   // reference to the function `new` associated with `Vec<T>`
<Vec<T> as SomeTrait>::some_fn
                // reference to the function `some_fn` associated with `SomeTrait`,
                //   as implemented by `Vec<T>`
T::size_of      // the function `size_of` associated with the type or trait `T`
<T>::size_of    // the function `size_of` associated with `T` _viewed as a type_
<T as SizeOf>::size_of
                // the function `size_of` associated with `T`'s impl of `SizeOf`

https://github.com/rust-lang/rfcs/blob/master/text/0195-associated-items.md#referencing-associated-items

  • 前半の:: 型パラメータ
  • 後半の:: structのfnへの参照
// handler.rs
pub trait FromRequest<S>: Sized {
    ...
    fn from_request(req: &HttpRequest<S>, cfg: &Self::Config) -> Self::Result;
    ...
    fn extract(req: &HttpRequest<S>) -> Self::Result {
        Self::from_request(req, &Self::Config::default())
    }
}

// extractor.rs
pub struct Path<T> {
    inner: T,
}

impl<T, S> FromRequest<S> for Path<T>
where
    T: DeserializeOwned,
{
    ...
    #[inline]
    fn from_request(req: &HttpRequest<S>, cfg: &Self::Config) -> Self::Result {
        let req = req.clone();
        let req2 = req.clone();
        let err = Rc::clone(&cfg.ehandler);
        de::Deserialize::deserialize(PathDeserializer::new(&req, cfg.decode))
            .map_err(move |e| (*err)(e, &req2))
            .map(|inner| Path { inner })
    }
}

https://github.com/actix/actix-web/blob/cc7f6b5eef4d9e2f67265f421e8d1e39fdf70168/src/extractor.rs

https://github.com/actix/actix-web/blob/cc7f6b5eef4d9e2f67265f421e8d1e39fdf70168/src/handler.rs

https://github.com/actix/examples/blob/4d26988edfc15b79ccd2edabb955cbcc975e361e/diesel/src/main.rs

    ...
    let manager = ConnectionManager::<SqliteConnection>::new("test.db");
    let pool = r2d2::Pool::builder()
        .build(manager)
        .expect("Failed to create pool.");
    ....

https://github.com/outersky/r2d2-mysql/tree/616b70b2aa27fac557777b242c52925d0a9896eb

    ...
    let manager = r2d2_mysql::MysqlConnectionManager::new(db_url).unwrap();
    let pool = Arc::new(r2d2::Pool::new(config, manager).unwrap());
    ...

Arc::new必要なのか🤔

// config.rs
pub struct Builder<M>
where
    M: ManageConnection,
{
    ...
}

impl<M> Builder<M>
where
    M: ManageConnection,
{
    ...
    pub fn build(self, manager: M) -> Result<Pool<M>, Error> {
        let pool = self.build_unchecked(manager);
        pool.wait_for_initialization()?;
        Ok(pool)
    }
    ...
}

// lib.rs
pub struct Pool<M: ManageConnection>(Arc<SharedPool<M>>);

最初から付いてる

https://github.com/sfackler/r2d2/blob/4b50b30b9dbd030f399e9f725dd1659e72e2f20f/src/lib.rs

https://github.com/sfackler/r2d2/blob/4b50b30b9dbd030f399e9f725dd1659e72e2f20f/src/config.rs

ご清聴

ありがとうございました

Made with Slides.com