RustConf 2019にいってきました

現地時間(PDT) 8/22 ~ 23、オレゴン州ポートランドで開催されたRustConfに参加してきたので、その模様を書いていこうと思います。

参加のきっかけ

Rustに関わっておられる方々がにどんな人達なのか実際に見てみたいと思い、ちょうどRustConfの開催時期に夏季休暇と有給で1週間休みがとれそうだったので、思い切っていってみることにしました。 一人海外旅行もアメリカも初めてでした。

道のり

成田空港からポートランド国際空港(PDX)まで、デルタ航空の直通便が就航しており、片道10時間程度です。時差はJST - 16時間。 会場はオレゴンコンベンションセンターで、空港からMaxLightRailという電車で20分程度の距離でした。 入国審査で、目的は観光で滞在日数は4日と答えたところ、"Very Short" と言われました。

f:id:yamaguchi7073xtt:20190824165539j:plain
portlandの場所

f:id:yamaguchi7073xtt:20190824170322j:plainf:id:yamaguchi7073xtt:20190824185813j:plain
PDXとオレゴンコンベンションセンターの入り口

1日目

RustConfは2日に渡って開催され、1日目は、いくつかのTraining Courseが用意されています。 あらかじめ、参加したいcourseのticketを購入しておく必要があり、今回はAsyncのcourseを選択しました。(これ以外は全て売り切れていました。)

f:id:yamaguchi7073xtt:20190824171920j:plainf:id:yamaguchi7073xtt:20190824173528j:plainf:id:yamaguchi7073xtt:20190824174103j:plain

Async Courseの内容は、Futureの概要/Conceptの説明や、async-stdのhandsonで、chatを作ってみるものでした。ちょうど、前日にasync/await syntaxがmergeされ、rustc 1.39.0-nightly (e44fdf979 2019-08-21) versionを利用しました。

f:id:yamaguchi7073xtt:20190824175016j:plain
`1.39`から`async/await`がstable !!

async-bookにそって進めていったのですが、よくあるsample codeのuseが漏れていて、book通りに進めていくとcompileが通らないことがおきました。するとすかさず(おそらく)参加者の一人の方がPRを送り(それがmergeされ)、「画面をリロードしてくれ、もう直ってるから」といって、sample codeのcompileが通るようになる場面がありました。

Rustの非同期関連については、まったくわかっておらず、今回のcourseの参加をきっかけに

あたりから読んでみようと思っています。

f:id:yamaguchi7073xtt:20190824184720j:plainf:id:yamaguchi7073xtt:20190824184712j:plain
irlnaさんによるasyncの冊子(ちなみに、2日目のspeakerでもあられる)

2日目

2日目が本番といったところで、参加者の人数は1日目よりはるかに多かったです。

f:id:yamaguchi7073xtt:20190824185618j:plain

f:id:yamaguchi7073xtt:20190824190055j:plain
開始前のkeynote会場(はじまると8,9割程度埋まっていました)

openingとclosingのkeynote以外は、2つの会場でSessionが行われ、各々好きなほうを聞きに行く形式でした。schedule

自分は以下のsessionに参加しました。sessionの内容はそのうちyoutubeにupされるかと思います。

特に印象的(理解できた)だったのは

  • mongoDBのGUIであるcompassのschema parser部分をperformanceをだすためにjsからrust/wasmを利用する構成に書き換えた話

  • Facebookで、Rustの導入に取り組まれているC歴30年の方が、Rustは今までで初めて、every roleでCを置き換えられる言語だ的なことをおっしゃっていたこと

f:id:yamaguchi7073xtt:20190824191845j:plain
rustの求人
f:id:yamaguchi7073xtt:20190824191913j:plain
awsにもrustの募集がある
f:id:yamaguchi7073xtt:20190824191839p:plain
sponsors

感想

実際にOpenSourceなprojectの活動に参加してみて、あらためて、こういった活動にContributeできるようなエンジニアになりたいという思いを持ちました。 (あとは、英語の冗談で笑えるようになりたい)

Rustでdoubly linked list

Rustでdoubly linked listを書いてみました。

use std::cell::RefCell;
use std::fmt;
use std::rc::Rc;

type Link<T> = Rc<RefCell<Node<T>>>;

#[derive(Debug)]
struct Node<T> {
    value: T,
    prev: Option<Link<T>>,
    next: Option<Link<T>>,
}

impl<T> Node<T> {
    fn new(value: T) -> Rc<RefCell<Self>> {
        Rc::new(RefCell::new(Self {
            value,
            prev: None,
            next: None,
        }))
    }
}

#[derive(Default)]
pub struct LinkedList<T> {
    head: Option<Link<T>>,
    tail: Option<Link<T>>,
    length: usize,
}

impl<T> LinkedList<T> {
    pub fn new() -> Self {
        Self {
            head: None,
            tail: None,
            length: 0,
        }
    }

    pub fn len(&self) -> usize {
        self.length
    }

    pub fn append(&mut self, v: T) {
        let node = Node::new(v);
        match self.tail.take() {
            Some(old_tail) => {
                old_tail.borrow_mut().next = Some(Rc::clone(&node));
                node.borrow_mut().prev = Some(old_tail);
            }
            None => {
                // first element
                debug_assert_eq!(self.len(), 0);
                self.head = Some(Rc::clone(&node));
            }
        }

        self.tail = Some(node);
        self.length += 1;
    }

    pub fn pop(&mut self) -> Option<T> {
        match self.tail.take() {
            Some(tail) => {
                if let Some(prev) = tail.borrow_mut().prev.take() {
                    prev.borrow_mut().next = None;
                    self.tail = Some(prev);
                } else {
                    // we take last element
                    debug_assert_eq!(self.len(), 1);
                    self.head = None;
                }
                self.length -= 1;
                let v = Rc::try_unwrap(tail) // Rc<RefCell<Node<T>> -> RefCell<Node<T>>
                    .ok() // Result<RefCell<Node<T>>, Rc<RefCell<Node<T>>>> -> Option<RefCell<Node<T>>>
                    .expect("Failed to Rc::try_unwrap tail node") // RefCell<Node<T>>
                    .into_inner() // RefCell<Node<T>> -> Node<T>
                    .value;
                Some(v)
            }
            None => None,
        }
    }

    pub fn iter(&self) -> Iter<T> {
        Iter {
            current: if self.len() == 0 {
                None
            } else {
                Some(Rc::clone(&self.head.as_ref().unwrap()))
            },
        }
    }
}

参照を相互に保持したい場合、Rc<RefCell<T>>でWrapしてやる必要があります。 Goならpointerを相互のfieldに代入すればよいだけなのですが、Rustの場合、Rcで参照の所有者が複数いることを、RefCellで、参照先からでも値が変更 できることを表現する必要があります。 型をみるだけで、この値は複数箇所で保持されていて、さらに変更されるうることまでわかりますね。 また、Rc::clone()node.clone()のようにmethod呼び出しではなく、Rc::clone(&node)のように明示的に、reference countedのcloneであることがわかるように呼ぶのが慣習だそうです。(node.clone()とあると、重たい処理かもしれないと思ってしまうからでしょうか。)

Rc<RefCell<T>>Tのfieldの所有権を奪おうとすると大変で、Rc::try_unwrap()して、RefCell<T>に変換してさらにRefCell::into_inner() で、T型にもどしてやる必要があります。

続いて、fmt::Debugを実装します。

impl<T: fmt::Display + Clone> fmt::Debug for LinkedList<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        let iter = self.iter();
        write!(f, "{{ head")?;
        for v in iter {
            write!(f, " -> {}", v)?;
        }
        write!(f, " }}")
    }
}

この実装がないと、println!("{:?}", list); としたときに、Node同士が相互参照しているので、循環参照してしまい、stackoverflowしてしまいます。 これを回避するには、どちからの参照をrc::Weakにしてもよいと思ったのですが、prev側を無視することにしました。

最後にiteratorを実装します。

impl<T: Clone> IntoIterator for LinkedList<T> {
    type Item = T;
    type IntoIter = Iter<T>;

    fn into_iter(self) -> Self::IntoIter {
        self.iter()
    }
}

pub struct Iter<T> {
    current: Option<Link<T>>,
}

impl<T: Clone> Iterator for Iter<T> {
    type Item = T;
    fn next(&mut self) -> Option<Self::Item> {
        match self.current.take() {
            None => None,
            Some(curr) => {
                let curr = curr.borrow();
                let v = curr.value.clone();
                match curr.next {
                    None => {
                        self.current = None;
                    }
                    Some(ref next) => {
                        self.current = Some(Rc::clone(next));
                    }
                }
                Some(v)
            }
        }
    }
}

impl<T: Clone> DoubleEndedIterator for Iter<T> {
    fn next_back(&mut self) -> Option<T> {
        match self.current.take() {
            None => None,
            Some(curr) => {
                let curr = curr.borrow();
                match curr.prev {
                    None => {
                        self.current = None;
                        None
                    }
                    Some(ref prev) => {
                        self.current = Some(Rc::clone(prev));
                        Some(prev.borrow().value.clone())
                    }
                }
            }
        }
    }
}

TにはClone boundを設けて楽をしました。DoubleEndedIteratorを実装すると

    #[test]
    fn reverse() {
        let mut list: LinkedList<i32> = LinkedList::new();
        (0..10).for_each(|n| list.append(n));

        let mut iter = list.iter();
        assert_eq!(iter.next(), Some(0));
        assert_eq!(iter.next(), Some(1));
        assert_eq!(iter.next(), Some(2));
        assert_eq!(iter.next(), Some(3));
        assert_eq!(iter.next_back(), Some(3));
        assert_eq!(iter.next_back(), Some(2));
        assert_eq!(iter.next_back(), Some(1));
        assert_eq!(iter.next_back(), Some(0));
        assert_eq!(iter.next_back(), None);
    }

このように、戻れるようになりました。 sourceはこちら

参考

Rust env_loggerの出力に色をつける

env_loggerの出力に色をつけたかったのですが、exampleが見つからず、docを読んだ結果以下のような処理になりました。

[dependencies]
log = "0.4.8"
env_logger = "0.6.2"
use env_logger::{fmt::Color, Builder};
use log::{Level,trace,debug,info,warn,error};
use std::io::Write;

fn init_logger() {
    let mut builder = Builder::new();

    builder.format(|buf, record| {
        let level_color = match record.level() {
            Level::Trace => Color::White,
            Level::Debug => Color::Blue,
            Level::Info => Color::Green,
            Level::Warn => Color::Yellow,
            Level::Error => Color::Red,
        };
        let mut level_style = buf.style();
        level_style.set_color(level_color);

        writeln!(
            buf,
            "{level} {file}:{line} {args}",
            level = level_style.value(record.level()),
            args = level_style.value(record.args()),
            file = level_style.value(&record.file().unwrap_or("____unknown")[4..]), // src/file.rs -> file.rs
            line = level_style.value(record.line().unwrap_or(0)),
        )
    });
    builder.filter(None, log::LevelFilter::Trace);
    builder.write_style(env_logger::WriteStyle::Auto);

    builder.init();
}

fn main() {
    init_logger();

    trace!("trace");
    debug!("debug");
    info!("info");
    warn!("warn");
    error!("error");
}