Skip to content

Instantly share code, notes, and snippets.

@hayajo
Last active June 24, 2019 07:24
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hayajo/0bccb071e2484b304dc7269db0d47ba6 to your computer and use it in GitHub Desktop.
Save hayajo/0bccb071e2484b304dc7269db0d47ba6 to your computer and use it in GitHub Desktop.
Rust超入門 #オンシャヘイシャ

Rust超入門 第3回

プログラミング言語Rust

ジェネリクス

ジェネリクス

  • <T>(Type)でジェネリックなデータ型を示す
    • Option<T>, Vec<T>, ...
    • 使い方
      struct Hoge<T> {
          foo: T,
          bar: T,
          buz: f64,
      }
      
      fn main() {
          // Hoge<i32>となる
          let hoge = Hoge {
              foo: 3,
              bar: 5,
              buz: 7.0,
          };
      
          println!("{}, {}, {:.*}", hoge.foo, hoge.bar, 2, hoge.buz);
      }
      

ジェネリック関数

  • 「Tに対してジェネリックである関数」

    fn takes_anything<T> (x: T) {
        // ...
    }
    
    • xはT型である
  • 複数のジェネリックな型を取る関数も可能

    fn takes_anything<T, U> (x: T, y : U) {
        // ...
    }
    

ジェネリック構造体

  • struct内にジェネリックな型を宣言する

    struct Point<T> {
        x: T,
        y: T,
    }
    
    fn main() {
        let int_point   = Point { x: 0,   y: 0 };  // T: i32
        let float_point = Point { x: 0.0, y: 0.0 };  // T: f64
    }
    

トレイト

ある型が提供しなければならない機能をコンパイラに伝える。

  • 型クラス
  • インターフェース

トレイトの宣言

  • メソッドのシグネチャを定義する
  • ある型のためにトレイトを実装する
trait HasArea {
    fn area(&self) -> f64;
}

struct Circle {
    x: f64,
    y: f64,
    r: f64,
}

impl HasArea for Circle {
    fn area(&self) -> f64 { // トレイト'HasArea'のareaシグネチャの実装
        std::f64::consts::PI * (self.r * self.r)
    }
}

fn main() {
    let c = Circle {
        x: 0.0,
        y: 0.0,
        r: 1.0,
    };

    assert_eq!("3.14", format!("{:.*}", 2, c.area()));
}

ジェネリック関数におけるトレイト境界

ジェネリックな型において、特定のメソッドの呼び出しはエラーとなります。

fn print_area<T>(shape: T) {
    println!("this shape has an area of {}", shape.area()); // T型がarea()を持っているかわからないのでエラー
}

トレイトを利用して、ある型における振る舞いの境界を追加する(制約を課る)ことが可能です。

fn print_area<T: HasArea>(shape: T) { // トレイト'HasArea'を実装する型に制約する
    println!("this shape has an area of {}", shape.area());
}

ジェネリック構造体におけるトレイト境界

型パラメータ(T)に境界を追加することができる(RectangleのT型はPartialEqトレイトを実装している必要がある)。

struct Rectangle<T> {
    x:      T,
    y:      T,
    width:  T,
    height: T,
}

// 構造体Rectangleの型パラメータTに境界を追加
impl <T: PartialEq> Rectangle<T> {
    fn is_square(&self) -> bool {
        self.width == self.height
    }
}

fn main() {
    let r = Rectangle {
        x:      0.0,
        y:      0.0,
        width:  3.0,
        height: 3.0,
    };
    assert!(r.is_square());

    // // PartialEqトレイトを実装していないパラメータの場合はエラーとなる
    // struct Hoge;
    // let r = Rectangle {
        // x:      Hoge{},
        // y:      Hoge{},
        // width:  Hoge{},
        // height: Hoge{},
    // };
    assert!(r.is_square());

}

トレイト実装のルール

  • 同じスコープに定義されていないトレイトは適用されない
    • 違うスコープのトレイトはuseする必要がある
  • トレイトやimplの対象となる型は実装元が同じでなければならない
    • 例えばi32ToStringトレイトはRustによって提供されているため、再実装できない

複数のトレイト境界

+で複数の制約を課することが可能です。

use std::fmt::Debug;

fn foo<T: Clone + Debug>(x: T) { // CloneとDebugトレイトの実装を要求
    let x1 = x.clone();
    println!("{:?}", x1);
}

Where節

トレイト境界はwhere節で表現することもできます。 記述が長くなる場合に有効です。

use std::fmt::{Display, Debug}; // Display, Debugトレイトを扱うためuseする必要がある

fn bar<T, K>(x: T, y: K) where
    T: Clone + Display,
    K: Clone + Debug {

    let x1 = x.clone();
    println!("{}", x1);

    let clone = y.clone();
    println!("{:?}", clone);
}

fn main() {
    bar(3, "Hello");
}

デフォルトメソッド

トレイトはデフォルト実装を定義できます。

trait Foo {
    fn is_valid(&self)   -> bool;
    fn is_invalid(&self) -> bool { !self.is_valid() }
}

struct Hoge;

impl Foo for Hoge {
    fn is_valid(&self) -> bool {
        true
    }
}

fn main() {
    let h = Hoge{};
    assert!(h.is_valid());
    assert!(!h.is_invalid());
}

継承

トレイトは継承可能です。

次の例の場合、HogeはBarの他にFooの実装もしなければなりません。

trait Foo {
    fn foo(&self) -> &Self;
}

trait Bar: Foo {
    fn bar(&self) -> &Self;
}

struct Hoge;

impl Foo for Hoge {
    fn foo(&self) -> &Hoge { println!("foo() called"); self }
}

impl Bar for Hoge {
    fn bar(&self) -> &Hoge { println!("bar() called"); self }
}

fn main() {
    let h = Hoge{};
    h.foo().bar();
}

Derive

特定のトレイトは[derive]アトリビュートでも定義できます。

use std::fmt::Debug;

#[derive(Debug, Clone)]
struct Hoge;

fn foo<T: Clone + Debug>(x: T) {
    let x1 = x.clone();
    println!("{:?}", x1);
}

fn main() {
    foo(&Hoge);
}
  • core::maker::Copy
  • core::clone::Clone
  • core::cmp::Eq
  • core::cmp::Ord
  • core::cmp::PartialEq
  • core::cmp::PartialOrd
  • core::fmt::Debug
  • core::fmt::Default
  • core::hash::Hash

トレイトオブジェクト

トレイトは基本的にコンパイラで静的にディスパッチ(静的ディスパッチ)されますですが、 Rustではトレイトオブジェクトと呼ばれるメカニズムで動的ディスパッチもサポートしています。

まずはサンプルのトレイトとその実装です。

trait Foo {
    fn method(&self) -> String;
}

impl Foo for u8 {
    fn method(&self) -> String { format!("u8: {}", *self) }
}

impl Foo for String {
    fn method(&self) -> String { format!("string: {}", *self) }
}

静的ディスパッチではつぎのようになります。

fn do_something<T: Foo>(x: T) {
    println!("{}", x.method());
}

fn main() {
    let x = 5u8;
    let y = "Hello".to_string();

    do_something(x);
    do_something(y);
}

静的ディスパッチされたコードのイメージはつぎのようになります。 コンパイル時に特殊化された関数(ここではdo_something_u8, do_something_string)がインライン展開されます。

fn do_something_u8(x: u8) {
    println!("{}", x.method());
}

fn do_something_string(x: String) {
    println!("{}", x.method());
}

fn main() {
    let x = 5u8;
    let y = "Hello".to_string();

    do_something_u8(x);
    do_something_string(y);
}

静的ディスパッチでは型ごとに同じ関数をコピーすることになり、コードの膨張が発生してしまします。

多くの場合静的ディスパッチが効率的ですが、 命令キャッシュの肥大化が問題となり、時として動的ディスパッチが効率的となる場合があります。

動的ディスパッチはトレイトオブジェクトと呼ばれる機能によって提供されます。

トレイトオブジェクトは&FooBox<Foo>と記述され、 指定されたトレイトを実装するあらゆる型の値を保持する通常の値 です。 実行時に正確な型が判明します。

fn do_something(x: &Foo) { // 通常の値として&Fooを受け取る
    println!("{}", x.method());
}

fn main() {
    let x = 5u8;
    let y = "Hello".to_string();

    do_something(&x as &Foo); // 1. トレイトを実装した型を指すポインタからキャストしてトレイトオブジェクトを取得する
    do_something(&y);         // 2. トレイトを実装した型を指すポインタを渡し、関数側で型強制する(最終的には1.と同じ)
}

すべてのトレイトがトレイトオブジェクトとして使えるわけではありません。 トレイトオブジェクトにできるのは下記の条件を満たしたオブジェクト安全なトレイトのみです。

  • トレイトがSelf: Sizedを要求しないこと(トレイトは実行時にどの構造体がくるかわからず、サイズを特定できないため)
  • トレイトのメソッドすべてがオブジェクト安全であること
    • Self: Sizedを要求しないこと
    • どのような型パラメータも持ってはならない
    • Selfを使ってはならない

ざっくりと言うと トレイトのメソッドでSelfを使うとオブジェクト安全ではなくなる とのことです。

クロージャ

fn main() {
    let plus_one = |x: i32| {
        x + 1
    };

    assert_eq!(2, plus_one(1));
}

関数とクロージャの構文の違い

fn plus_one    (x: i32) -> i32 { x + 1 };
let plus_one = |x: i32| -> i32 { x + 1 };
let plus_one = |x: i32| -> x + 1;

クロージャとクロージャの環境

クロージャではそれを囲んでいるスコープの束縛を含むことができます。

fn main() {
    let num = 5;
    let plus_num = |x: i32| x + num; // 束縛numを参照

    assert_eq!(6, plus_num(1));
}

ですが、クロージャでは 束縛を借用します 。つぎのようなコードはエラーとなります。

fn main() {
    let num = 5;
    let plus_num = |x: i32| x + num; // 束縛numを参照

    let y = &mut num; // エラー: 束縛numのミュータブルな参照

    assert_eq!(6, plus_num(1));
}

moveクロージャ

moveキーワードでクロージャに環境の所有権を取得することを強制できます。

fn main() {
    let mut num = 5;
    {
        let mut add_num = move |x: i32| num += x; // i32はCopyトレイトを実装しているので、コピーされた値の所有権がmoveされる
        add_num(5);
    }
    assert_eq!(5, num);
}

クロージャを引数に取る

クロージャは実質的にトレイトへのシンタックスシュガーとなります。 クロージャを引数に取る場合はトレイトと同様に行います。

fn main() {
    fn call_with_one<F>(some_closure: F) -> i32 where
        F: Fn(i32) -> i32 { // Where節でトレイト境界を定義
            some_closure(1)
        }

    let answer = call_with_one(|x| x + 2); // トレイト境界を満たすクロージャを引数として渡す

    assert_eq!(3, answer);
}

動的ディスパッチの場合はトレイトオブジェクトとして扱います。

fn main() {
    // トレイトオブジェクトを引数に取る
    fn call_with_one(some_closure: &Fn(i32) -> i32) ->i32 {
        some_closure(1)
    }

    let answer = call_with_one(&|x| x + 2); // クロージャの参照を渡して関数側で型強制する

    assert_eq!(3, answer);
}

関数ポインタとクロージャ

クロージャを引数として期待している関数に関数ポインタを渡すことができます。

fn main() {
    fn call_with_one(some_closure: &Fn(i32) -> i32) ->i32 {
        some_closure(1)
    }

    fn add_two(x: i32) -> i32 {
        x + 2
    }

    let answer = call_with_one(&add_two); // 関数ポインタを渡す

    assert_eq!(3, answer);
}

クロージャを返す

クロージャ(Fn)はトレイトなのでサイズが不定となり、そのまま返すことはできません。 またクロージャは環境別にstructを生成するため、それぞれの型は匿名となり、帰り値の型を一致しません。

そこでFnをボックス化(ヒープに確保)してトレイトオブジェクトを返します。 またクロージャでは束縛を借用するので、moveクロージャとすることで参照ではなく所有権の移動します。

fn main() {
    fn factory() -> Box<Fn(i32) -> i32> { // クロージャをヒープに確保した上で返す
        let num = 5;
        Box::new(move |x| x + num) // numの所有権を移動する
    }

    let f = factory(); // クロージャを受け取る

    let answer = f(1);
    assert_eq!(6, answer);
}

conststatic

const

定数を定義します。型を明示する必要があります。プログラム全体のライフタイムの間生きます。

定数はコンパイラでインライン化されるため、同じ定数への参照が同じアドレスを指しているとは限らないので注意してください。

const N: i32 = 5;

static

グローバル変数。型を明示する必要があります。プログラム全体のライフタイムの間生きます。

定数と違いコンパイラでインライン化されず、ただひとつのインスタンスのみが存在します。

static N: i32 = 5;
static NAME: &'static str = "Taro";

staticな変数もmutでミュータブルにできますが、スレッドセーフではなくなるためunsafeブロック内でアクセスする必要があります。

fn main() {
    static mut HELLO: &'static str = "Hello";

    // assert_eq!("Hello", HELLO); unsaafeブロック内ではないのでエラー

    unsafe {
        assert_eq!("Hello", HELLO);

        HELLO = "こんにちは";
        assert_eq!("こんにちは", HELLO);
    }
}

type エイリアス

typeキーワードで型へのエイリアスを宣言できます。

fn main() {
    type Num = i32;

    let x: i32 = 3;
    let y: Num = 3;

    assert_eq!(y, x);
}

Rust超入門 第4回

プログラミング言語Rust

関連型

トレイトの関数で利用する任意の型(N, E, etc.)はtypeで定義します。

trait Graph {
    type N; // 関連型N
    type E; // 関連型E

    fn has_edge(&self, &Self::N, &Self::N) -> bool;
    fn edges(&self, &Self::N) -> Vec<Self::E>;
}

struct Node;
struct Edge;
struct MyGraph;

impl Graph for MyGraph {
    type N = Node; // 関連型NをNodeとする
    type E = Edge; // 関連型EをEdgeとする

    fn has_edge(&self, n1: &Node, n2: &Node) -> bool {
        true
    }

    fn edges(&self, n: &Node) -> Vec<Edge> {
        Vec::new()
    }
}

fn main() {
    // Graphトレイトを実装した値の参照と、Graphトレイトの関連型Nの参照をふたつ(start, end)を引数にとる関数
    fn distance<G: Graph>(graph: &G, start: &G::N, end: &G::N) -> u32 {
        0
    }

    distance(&MyGraph{}, &Node{}, &Node{});
}

もし関連型として定義しない場合はこんな感じになって記述が面倒です。

trait Graph<N, E> {
    fn has_edge(&self, &N, &N) -> bool;
    fn edges(&self, &N) -> Vec<E>;
}

struct Node;
struct Edge;
struct MyGraph;

impl Graph<Node, Edge> for MyGraph {
    fn has_edge(&self, n1: &Node, n2: &Node) -> bool {
        true
    }

    fn edges(&self, n: &Node) -> Vec<Edge> {
        Vec::new()
    }
}

fn main() {
    // 面倒な記述ですね
    fn distance<N, E, G: Graph<N, E>>(graph: &G, start: &N, end: &N) -> u32 {
        0
    }

    distance(&MyGraph{}, &Node{}, &Node{});
}

演算子とオーバーロード

演算子のオーバーロードは特定のトレイトを実装することで可能です。

オーバーロード可能な演算子は std::ops - Rust を参照してください。

use std::ops::Add;

#[derive(Debug)]
struct Point {
    x: i32,
    y: i32,
}

// Addトレイトを実装して `+` 演算子をオーバーロードする
impl Add for Point {
    type Output = Point;

    fn add(self, other: Point) -> Point {
        Point { x: self.x + other.x, y: self.y + other.y }
    }
}

fn main() {
    let p1 = Point { x: 1, y: 0 };
    let p2 = Point { x: 2, y: 3 };

    let p3 = p1 + p2; // Pointのaddが呼ばれる

    println!("{:?}", p3);
}

マクロ

マクロは展開された構文への短縮表現です。 展開はコンパイルの初期段階、すべての静的なチェックが実行される前に行われます。

マクロのコードは理解しづらい上にコンパイルエラーは展開したコードの中の問題について書かれるため、マクロを定義する場合は注意が必要です。

vec!マクロの例です。

fn main() {
    let x: Vec<u32> = vec![1, 2, 3];

    print!("{:?}", x);
}

上記マクロはつぎのコードの構文上の短縮表現といえます。

fn main() {
    let x: Vec<u32> = {
        let mut temp_vec = Vec::new();
        temp_vec.push(1);
        temp_vec.push(2);
        temp_vec.push(3);
        temp_vec
    };

    print!("{:?}", x);
}

このvec!マクロの実装はつぎのようになります。

macro_rules! myvec { // `myvec!`マクロを定義する
    ( $( $x: expr ),* ) => { // パターンマッチ。0個以上のカンマで句切られた式をメタ変数`$x`に束縛する
        { // 展開結果の一部となる括弧(上の括弧は`macro_rules!`の構文の括弧)
            let mut temp_vec = Vec::new();
            $( // $(...)*  で、含んでいる$xの数だけ足並みを揃えて繰り返す
                temp_vec.push($x);
            )*
            temp_vec
        }
    };
}

fn main() {
    let x: Vec<u32> = myvec![1, 2, 3];

    print!("{:?}", x);
}

Unsafe

コンパイラでは安全と検証できないプログラムを書くためにはunsafeキーワードを使います。

unsafeキーワードは4つの場面で使われます。

// 関数
unsafe fn danger_will_robinson() {
    // 恐ろしいもの
}

// ブロック
unsafe {
    // 恐ろしいもの
}

// トレイト
unsafe trait Scary {
    // 恐ろしいもの
}

// unsafeトレイトの実装
unsafe impl Scary for i32 {
    // 恐ろしいもの
}

unsafe関数、ブロックでは下記の3つのことが可能となります。

  • ミュータブルなstatic変数の読み取りと更新

    fn main() {
        static mut HELLO: &'static str = "Hello";
    
        // assert_eq!("Hello", HELLO); unsaafeブロック内ではないのでエラー
    
        unsafe {
            assert_eq!("Hello", HELLO);
    
            HELLO = "こんにちは";
            assert_eq!("こんにちは", HELLO);
        }
    }
    
  • Rawポインタのデリファレンス

    fn main() {
        let i: u32 = 1;
        let p_imm: *const u32 = &i as *const u32;
    
        let mut m: u32 = 2;
        let p_mut: *mut u32 = &mut m;
    
        unsafe {
            let ref_imm: &u32 = &*p_imm;
            let ref_mut: &mut u32 = &mut *p_mut;
        }
    }
    
  • アンセーフ関数の呼び出し

    extern crate libc;
    
    use libc::pid_t;
    
    fn pid() -> pid_t {
        unsafe { libc::getpid() }
    }
    
    fn main() {
        println!("{}", pid());
    }
    

エラー処理

Rustでは「エラーの可能性がある」場合の関数の戻り型としてstd::result::Result列挙型が使われます。

use std::fs::File;

fn main() {
    let ret = File::open("hoge.txt");
    let mut file = match ret {
        Ok(file) => { file },                   // 成功時のstd::result::Resultのヴァリアント `Ok`
        Err(why) => { panic!(why.to_string()) } // 失敗時のstd::result::Resultのヴァリアント `Err`
    };
}

毎回matchするのは手間なので、Okを取得する場合はunwrap()を使います。

use std::fs::File;

fn main() {
    let filename = "hoge.txt";
    let mut file = File::open(filename).unwrap(); // Err(E)の場合にpanicする(Eを"{:?}"で表示)
}

エラーメッセージを指定する場合はecpect(&str)を使います。

use std::fs::File;

fn main() {
    let filename = "hoge.txt";
    let mut file = File::open(filename).expect(&format!("Failed to open file: {}", filename));
}

std::result::Resultには他にも便利なチェック関数があるのでドキュメントを参照してください。

Rust超入門 おまけ

hyperを使った超簡単なHTTPサーバー

準備

cargoでプロジェクトを作成します。

$ cargo new mywebapp --bin
$ cd mywebapp
$ tree --charset=ascii
.
|-- Cargo.toml
`-- src
    `-- main.rs

一度実行してみましょう。

$ cargo run
   Compiling mywebapp v0.1.0 (file:///Users/hayajo/Downloads/mywebapp)
     Running `target/debug/mywebapp`
Hello, world!

hyperを使う

hyperはHTTPクライアント/サーバーのライブラリクレートです。 cargoで取得できます。

まずはCargo.tomlファイルの[dependencies]を次のように変更してプロジェクトの依存ライブラリにhyperを追加します。

[dependencies]
hyper = "0.9.1"

つぎにsrc/main.rsファイルをhyperを使ったコードに変更します。

extern crate hyper; // 外部クレートのインポートを宣言する

use hyper::server::{Server, Request, Response}; // このスコープにシンボルをインポートする

fn main() {
    // クロージャでハンドラを定義する
    let handler = |_: Request, response: Response| response.send("Hello World\n".as_bytes()).unwrap();

    let server = Server::http("0.0.0.0:3000").unwrap();
    server.handle(handler).unwrap(); // ハンドラを指定してサーバーを開始する
}

cargo runで実行してみましょう。 自動的にライブラリの依存が解決された後、プログラムのコンパイルと実行が行われます。

$ cargo run

別シェルでサーバーにアクセスしましょう。 Hello World が表示されれば成功です。

$ curl localhost:3000
Hello World

ハンドラがクロージャのままでは扱いづらいので、handler関数に分けましょう。

extern crate hyper;

use hyper::server::{Server, Request, Response};

fn handler(_: Request, response: Response) {
    response.send("Hello World\n".as_bytes()).unwrap();
}

fn main() {
    let server = Server::http("0.0.0.0:3000").unwrap();
    server.handle(handler).unwrap();
}

リクエストURIによって処理を変更する

リクエストURIによって処理を変えてみましょう。

リクエストURIはhyper::server::Requesturiフィールドで取得できます。 値はhyper::uri::RequestUriのEnum型です。

今回の実装では値がAbsolutePathの場合はその値を、AbsoluteUriの場合はその値であるhyper::Urlのpath()の値を、それ以外の場合はエラーとして処理を終了します。

extern crate hyper;

use hyper::server::{Server, Request, Response};

fn handler(request: Request, mut response: Response) { // エラー時にステータスコードを変更するので`mut response`とする
    use hyper::uri::RequestUri::*;

    let uri = request.uri.clone(); // 後ほど各ハンドラにrequest, responseの所有権を渡すので、ここではcloneした値を利用する
    let path = match uri {
        AbsolutePath(ref s) => s.as_str(), // AbsolutePath(s)では束縛とともにuriのオーナーシップを取得してしまう(bind-by-value)ため、`ref`キーワードでリファレンスを束縛します(bind-by-reference)
        AbsoluteUri(ref u)  => u.path(),   // 同上
        _                   => {
            println!("Invalid Request Uri");
            *response.status_mut() = hyper::status::StatusCode::BadRequest; // ステータスコードを変更する
            response.send(b"error").unwrap();
            return;
        }
    };

    response.send(format!("PATH: {}\n", path).as_bytes()).unwrap();
}

fn main() {
    let server = Server::http("0.0.0.0:3000").unwrap();
    server.handle(handler).unwrap();
}

実行してアクセスしてみましょう。

$ cargo run
$ curl localhost:3000
PATH: /
$ curl localhost:3000/hoge/fuga
PATH: /hoge/fuga
$ nc localhost 3000
GET http://www.example.com/foo/bar HTTP/1.1

HTTP/1.1 200 OK
Content-Length: 15
Date: Wed, 17 Aug 2016 07:06:54 GMT

PATH: /foo/bar
$ nc localhost 3000
CONNECT www.example.com:80 HTTP/1.1

HTTP/1.1 400 Bad Request
Content-Length: 5
Date: Wed, 17 Aug 2016 07:27:40 GMT

error

簡単なハンドラをいくつか用意して、リクエストURIによって処理を切り替えてみましょう。

extern crate hyper;

use hyper::server::{Server, Request, Response};

fn greet_handler(_: Request, response: Response) {
    response.send("Hello World\n".as_bytes()).unwrap();
}

fn default_handler(request: Request, response: Response) {
    response.send(format!("PATH: {}\n", request.uri).as_bytes()).unwrap();
}

fn handler(request: Request, mut response: Response) { // エラー時にステータスコードを変更するので`mut response`とする
    use hyper::uri::RequestUri::*;

    let uri = request.uri.clone(); // 後ほど各ハンドラにrequest, responseの所有権を渡すので、ここではcloneした値を利用する
    let path = match uri {
        AbsolutePath(ref s) => s.as_str(), // AbsolutePath(s)では束縛とともにuriのオーナーシップを取得してしまう(bind-by-value)ため、`ref`キーワードでリファレンスを束縛します(bind-by-reference)
        AbsoluteUri(ref u)  => u.path(),   // 同上
        _                   => {
            println!("Invalid Request Uri");
            *response.status_mut() = hyper::status::StatusCode::BadRequest;
            response.send(b"error").unwrap();
            return;
        }
    };

    match path {
        "/greet" => greet_handler(request, response),
        _        => default_handler(request, response),
    }
}

fn main() {
    let server = Server::http("0.0.0.0:3000").unwrap();
    server.handle(handler).unwrap();
}

実行してアクセスしてみましょう。

$ cargo run
$ curl localhost:3000
PATH: /
$ curl localhost:3000/greet
Hello World
$ nc localhost 3000
GET http://www.example.com/foo/bar HTTP/1.1

HTTP/1.1 200 OK
Content-Length: 15
Date: Wed, 17 Aug 2016 07:06:54 GMT

PATH: /foo/bar
$ nc localhost 3000
CONNECT www.example.com:80 HTTP/1.1

HTTP/1.1 400 Bad Request
Content-Length: 5
Date: Wed, 17 Aug 2016 07:27:40 GMT

error

というわけで

let, mut, match, refキワード, unwrap, 外部クレートの利用などを使ったサンプルでした。

本当はもっと凝ったことしたかったのですが、今回はこんなもので...

@hayajo
Copy link
Author

hayajo commented Aug 15, 2016

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment