{"action":"created","comment":{"url":"https://api.github.com/repos/rust-lang/blog.rust-lang.org/pulls/comments/28575366","id":28575366,"diff_hunk":"@@ -0,0 +1,613 @@\n+---\n+layout: post\n+title: \"Mixing matching, mutation, and moves in Rust\"\n+author: Felix S. Klock II\n+description: \"A tour of matching and enums in Rust.\"\n+---\n+\n+One of the primary goals of the Rust project is to enable safe systems\n+programming. Systems programming usually implies imperative\n+programming, which in turns often implies side-effects, reasoning\n+about shared, aliasable state, et cetera.\n+\n+At the same time, to provide *safety*, Rust programs and data types\n+must be structured in a way that allows static checking to ensure\n+soundness. Rust has features and restrictions that operate in tandem\n+to ease writing programs that can pass these checks and thus ensure\n+safety. For example, Rust incorporates the notion of *ownership* deeply\n+into the language.\n+\n+Rust's `match` expression is a construct that offers an interesting\n+combination of such features and restrictions. A `match` expression\n+takes an input value, classifies it, and then jumps to code written to\n+handle the identified class of data.\n+\n+In this post we explore how Rust processes such data via `match`.\n+The crucial elements that `match` and its counterpart `enum` tie\n+together are:\n+\n+* Structural pattern matching of algebraic data types: The `match`\n+  construct enables case analysis with ergonomics that are vastly\n+  improved over that provided by a C or Java style `switch` statement.\n+\n+* Exhaustive case analysis, which ensures that no case is omitted\n+  when processing an input.\n+\n+* `match` embraces both imperative and functional styles of\n+  programming: The compiler's static analyses work hard to ensure\n+  statement-oriented programming remains palatable, rather than\n+  forcing everyone to adopt an expression-oriented mindset.\n+\n+* Destructuring bind of *L-values*: Rust encourages the developer to\n+  think carefully about ownership and borrowing. To ensure that\n+  processing data does not force one to give up ownership of a value\n+  prematurely, `match` is designed with support for merely *borrowing*\n+  substructure within its input (as opposed to always *moving* such\n+  substructure).\n+\n+We cover each of the items above in detail below, but first we\n+establish a foundation for the discussion: What does `match` look\n+like, and how does it work?\n+\n+### The Basics of `match`\n+\n+The `match` expression in Rust has this form:\n+\n+```rust\n+match INPUT_EXPRESSION {\n+    PREDICATE_1 => RESULT_EXPRESSION_1,\n+    PREDICATE_2 => RESULT_EXPRESSION_2,\n+    ...\n+    PREDICATE_n => RESULT_EXPRESSION_n\n+}\n+```\n+\n+where each of the `PREDICATE_i` contains at least one *pattern*. A\n+pattern describes a subset of the possible values to which\n+`INPUT_EXPRESSION` could evaluate.\n+The syntax `PREDICATE => RESULT_EXPRESSION` is called a \"match arm\",\n+or simply \"arm\".\n+\n+Patterns can match atomic values, like integers or characters; they\n+can also match user-defined symbolic data, defined via `enum`.\n+\n+The below code demonstrates generating the next guess (poorly) in a number\n+guessing game, given the answer from a previous guess.\n+\n+(Incidentally, nearly all the code in this post is directly\n+executable; you can cut-and-paste the code snippets into a file\n+`demo.rs`, compile the file with `--test`, and run the resulting\n+binary to see the tests run.)\n+\n+```rust\n+enum Answer {\n+    Higher,\n+    Lower,\n+    Bingo,\n+}\n+\n+fn suggest_guess(prior_guess: u32, answer: Answer) {\n+    match answer {\n+        Answer::Higher => println!(\"maybe try {} next\", prior_guess + 10),\n+        Answer::Lower  => println!(\"maybe try {} next\", prior_guess - 1),\n+        Answer::Bingo  => println!(\"we won with {}!\", prior_guess),\n+    }\n+}\n+\n+#[test]\n+fn demo_suggest_guess() {\n+\tsuggest_guess(10, Answer::Higher);\n+\tsuggest_guess(20, Answer::Lower);\n+\tsuggest_guess(19, Answer::Bingo);\n+}\n+```\n+\n+Patterns can also match structured data (e.g. tuples, slices, user-defined\n+data types) via corresponding patterns. In such patterns, one often\n+binds substructure of the input to local variables (identifier patterns),\n+for use either in the arm's predicate or in its result.\n+\n+The special `_` pattern matches any single value, and is often used as\n+a catch-all; the special `..` pattern generalizes this by matching any\n+*series* of values or name/value pairs.\n+\n+Also, one can collapse multiple patterns into one arm by separating the\n+patterns by vertical bars (`|`); thus that arm matches either this pattern,\n+or that pattern, et cetera.\n+\n+These features are illustrated in the following revision to the\n+guessing-game answer generation strategy:\n+\n+```rust\n+struct GuessState {\n+    guess: u32,\n+    answer: Answer,\n+    low: u32,\n+    high: u32,\n+}\n+\n+fn suggest_guess_smarter(s: GuessState) {\n+    match s {\n+        GuessState { answer: Answer::Bingo, guess: p, .. } => {\n+            println!(\"we won with {}!\", p);\n+        }\n+        GuessState { answer: Answer::Higher, guess: l, low: _, high: h  } |\n+        GuessState { answer: Answer::Lower,  guess: h, low: l, high: _ } => {\n+            let mid = l + ((h - l) / 2);\n+            println!(\"lets try {} next\", mid);\n+        }\n+    }\n+}\n+\n+#[test]\n+fn demo_guess_state() {\n+\tsuggest_guess_smarter(GuessState {\n+\t\tguess: 20, answer: Answer::Lower, low: 10, high: 1000\n+\t});\n+}\n+```\n+\n+This ability to simultaneously perform case analysis *and* bind input\n+substructure leads to powerful, clear, and concise code, focusing the\n+reader's attention directly on the data relevant to the case at hand.\n+\n+That is `match` in a nutshell.\n+\n+So, what is the interplay between this construct and Rust's approach to\n+ownership and safety in general?\n+\n+### Exhaustive case analysis\n+\n+`match` enforces exhaustive case analysis, which helps catch bugs in\n+program logic and ensures that the value of a match expression is\n+well-defined.\n+\n+Case analysis is an important problem solving technique: Dividing a\n+problem into some number of separate cases, and then analyzing each\n+case individually.\n+\n+For this method of problem solving to work, the cases must be\n+*collectively exhaustive*; otherwise, a case that was not covered\n+would mean a potential problem instance for which no solution has been\n+identified.\n+\n+This brings us to one of the fundamental restrictions of Rust's\n+`match` construct: the collection of cases must be exhaustive. In\n+other words, every possible input value must be covered by the pattern\n+for a least one arm in the match.\n+\n+So, for example, the following code is rejected at compile-time.\n+\n+```rust\n+fn suggest_guess_broken(prior_guess: u32, answer: Answer) {\n+    let next_guess = match answer {\n+        Answer::Higher => prior_guess + 10,\n+        Answer::Lower  => prior_guess - 1,\n+        // ERROR: non-exhaustive patterns: `Bingo` not covered\n+    };\n+    println!(\"maybe try {} next\", next_guess);\n+}\n+```\n+\n+Many other languages offer a pattern matching construct (ML and\n+various macro-based `match` implementations in Scheme both come to\n+mind), but not all of them have this restriction.\n+\n+Rust has this restriction for two reasons:\n+\n+* First, as noted above, dividing a problem into cases only yields a\n+general solution if the cases are exhaustive. Exhaustiveness-checking\n+exposes logical errors.\n+\n+* Second, since `match` is an expression form, exhaustiveness ensures\n+that such expressions always evaluates to a value of the correct type\n+(or jump elsewhere in the program, as illustrated here):\n+\n+```rust\n+fn suggest_guess_fixed(prior_guess: u32, answer: Answer) {\n+    let next_guess = match answer {\n+        Answer::Higher => prior_guess + 10,\n+        Answer::Lower  => prior_guess - 1,\n+        Answer::Bingo  => {\n+            println!(\"we won with {}!\", prior_guess);\n+            return;\n+        }\n+    };\n+    println!(\"maybe try {} next\", next_guess);\n+}\n+\n+#[test]\n+fn demo_guess_fixed() {\n+\tsuggest_guess_fixed(10, Answer::Higher);\n+\tsuggest_guess_fixed(20, Answer::Lower);\n+\tsuggest_guess_fixed(19, Answer::Bingo);\n+}\n+```\n+\n+### Algebraic Data Types and Data Invariants\n+\n+Algebraic data types succinctly describe classes of data and allow one\n+to encode rich structural invariants.\n+\n+An `enum` type allows one to define mutually-exclusive classes of\n+values. The examples shown above used `enum` for simple symbolic tags,\n+but in Rust, such definitions can define much richer classes of data.\n+\n+For example, a binary tree is either a leaf, or an internal node with\n+references to two child trees. Here is one way to encode a tree of\n+integers in Rust:\n+\n+```rust\n+enum BinaryTree {\n+    Leaf(i32),\n+    Node(Box<BinaryTree>, i32, Box<BinaryTree>)\n+}\n+```\n+\n+(The `Box<V>` type describes an owning reference to a heap-allocated\n+instance of `V`; if you own a `Box<V>`, then you also own the `V` it\n+contains, and can mutate it, lend out references to it, et cetera, and\n+when you finish with the box and let it fall out of scope, it will\n+automatically clean up the resources associated with the\n+heap-allocated `V`.)\n+\n+The above definition ensures that if we are given a `BinaryTree`, it\n+will always fall into one of the above two cases. One will never\n+encounter a `BinaryTree::Node` that does not have a left-hand child.\n+There is no need to check for null.\n+\n+One *does* need to check whether a given `BinaryTree` is a `Leaf` or\n+is a `Node`, but the compiler statically ensure such checks are done:\n+you cannot accidentally interpret the data of a `Leaf` as if it were a\n+`Node`, nor vice versa.\n+\n+```rust\n+/// Sum of values in all the nodes and leaves of `t`.\n+fn tree_weight_v1(t: BinaryTree) -> i32 {\n+    match t {\n+        BinaryTree::Leaf(payload) => payload,\n+        BinaryTree::Node(left, payload, right) => {\n+            tree_weight_v1(*left) + payload + tree_weight_v1(*right)\n+        }\n+    }\n+}\n+\n+/// Looks like:\n+///\n+/// (4)\n+///  |\n+///  +--(2)\n+///  |   |\n+///  |   +--[1]\n+///  |   |\n+///  |   +--[3]\n+///  |\n+///  +--[5]\n+///\n+fn sample_tree() -> BinaryTree {\n+    let l1 = Box::new(BinaryTree::Leaf(1));\n+    let l3 = Box::new(BinaryTree::Leaf(3));\n+    let n2 = Box::new(BinaryTree::Node(l1, 2, l3));\n+    let l5 = Box::new(BinaryTree::Leaf(5));\n+\n+    BinaryTree::Node(n2, 4, l5)\n+}\n+\n+#[test]\n+fn tree_demo_1() {\n+    let tree = sample_tree();\n+    assert_eq!(tree_weight_v1(tree), (1 + 2 + 3) + 4 + 5);\n+}\n+```\n+\n+### Both expression- and statement-oriented\n+\n+Unlike many languages that offer pattern matching, Rust *embraces*\n+both statement- and expression-oriented programming.\n+\n+Many functional languages that offer pattern matching encourage one to\n+write in an \"expression-oriented style\", where the focus is always on\n+the value returned by combining expressions and evaluating them, and\n+side-effects are discouraged. This style contrasts with imperative\n+languages, which encourage a statement-oriented style that focuses on\n+sequences of commands executed solely for their side-effects.\n+\n+Consider writing a function which maps a non-negative integer to a\n+string rendering it as an ordinal (\"1st\", \"2nd\", \"3rd\", ...).\n+\n+The following code uses range patterns to simplify things, but also,\n+it is written in a style similar to a `switch` in a statement-oriented\n+language like C (or C++, Java, et cetera), where the arms of the\n+`match` are executed for their side-effect alone:\n+\n+```rust\n+fn num_to_ordinal(x: u32) -> String {\n+    let suffix;\n+    match (x % 10, x % 100) {\n+        (1, 1) | (1, 21...91) => {\n+            suffix = \"st\";\n+        }\n+        (2, 2) | (2, 22...92) => {\n+            suffix = \"nd\";\n+        }\n+        (3, 3) | (3, 23...93) => {\n+            suffix = \"rd\";\n+        }\n+        _                     => {\n+            suffix = \"th\";\n+        }\n+    }\n+    return format!(\"{}{}\", x, suffix);\n+}\n+\n+#[test]\n+fn test_num_to_ordinal() {\n+    assert_eq!(num_to_ordinal(   0),    \"0th\");\n+    assert_eq!(num_to_ordinal(   1),    \"1st\");\n+    assert_eq!(num_to_ordinal(  12),   \"12th\");\n+    assert_eq!(num_to_ordinal(  22),   \"22nd\");\n+    assert_eq!(num_to_ordinal(  43),   \"43rd\");\n+    assert_eq!(num_to_ordinal(  67),   \"67th\");\n+    assert_eq!(num_to_ordinal(1901), \"1901st\");\n+}\n+```\n+\n+The Rust compiler accepts the above program; this is notable because\n+its static analysis is ensuring both that `suffix` is always\n+initialized before we run the `format!` at the end *and* that `suffix`\n+is assigned at most once during the function's execution (because if\n+we could assign `suffix` multiple times, the compiler would force us\n+to mark `suffix` as mutable).\n+\n+To be clear, the above program certainly *can* be written in an\n+expression-oriented style in Rust; for example, like so:\n+\n+```rust\n+fn num_to_ordinal_expr(x: u32) -> String {\n+    format!(\"{}{}\", x, match (x % 10, x % 100) {\n+        (1, 1) | (1, 21...91) => \"st\",\n+        (2, 2) | (2, 22...92) => \"nd\",\n+        (3, 3) | (3, 23...93) => \"rd\",\n+        _                     => \"th\"\n+    })\n+}\n+```\n+\n+Sometimes expression-oriented style can yield very succinct code;\n+other times the style requires contortions that can be\n+avoided by writing in a statement-oriented style.\n+\n+Each of the styles has its use cases. Crucially, switching to a\n+statement-oriented style in Rust does not sacrifice every other\n+feature that Rust provides, such as the guarantee that a non-`mut`\n+binding is assigned at most once.\n+\n+An important case where this arises is when one wants to\n+initialize some state and then borrow from it, but only on\n+*some* control-flow branches.\n+\n+```rust\n+fn sometimes_initialize(input: i32) {\n+    let string: String; // a dynamically-constructed string value\n+    let borrowed: &str; // a reference to string data\n+    match input {\n+        0...100 => {\n+\t\t\t// Construct a String on the fly...\n+            string = format!(\"input prints as {}\", input);\n+\t\t\t// ... and then borrow from inside it.\n+            borrowed = &string[6..];\n+        }\n+        _ => {\n+\t\t\t// String literals are *already* borrowed references\n+            borrowed = \"expected between 0 and 100\";\n+        }\n+    }\n+    println!(\"borrowed: {}\", borrowed);\n+\n+    // Below would cause compile-time error if uncommented...\n+\n+\t// println!(\"string: {}\", string);\n+\n+\t// ...namely: error: use of possibly uninitialized variable: `string`\n+}\n+\n+#[test]\n+fn demo_sometimes_initialize() {\n+    sometimes_initialize(23);  // this invocation will initialize `string`\n+    sometimes_initialize(123); // this one will not\n+}\n+```\n+\n+The interesting thing about the above code is that after the `match`,\n+we are not allowed to directly access `string`, because the compiler\n+requires that the variable be initialized on every path through the\n+program. At the same time, we *can*, via `borrowed`, access the data that\n+is held *within* `string`, because a reference to that data is held by the\n+`borrowed` variable when we go through the first match arm, and we\n+ensure `borrowed` itself is initialized on every execution path\n+through the program that reaches the `println!` that uses `borrowed`.\n+\n+(The compiler ensures that no outstanding borrows of the\n+`string` data could possible outlive `string` itself, and the\n+generated code ensures that at the end of the scope of `string`, its\n+data is deallocated if it was previously initialized.)\n+\n+In short, for soundness, the Rust language ensures that data is always\n+initialized before it is referenced, but the designers have strived to\n+avoid requiring artificial coding patterns inserted solely to placate\n+Rust's static analyses (such as requiring one to initialize `string`\n+above with some dummy data just so that it can be borrowed later).\n+\n+### Matching L-values\n+\n+Matching an input can *borrow* input substructure, without taking\n+ownership; this is crucial for matching a reference (e.g. a value of\n+type `&T`).\n+\n+The \"Algebraic Data Types\" section above described a tree datatype, and\n+showed a program that computed the sum of the integers in a tree\n+instance.\n+\n+That version of `tree_weight` has one big downside, however: it takes\n+its input tree by value. Once you pass a tree to `tree_weight_v1`, that\n+tree is *gone* (as in, deallocated).\n+\n+```rust\n+#[test]\n+fn tree_demo_v1_fails() {\n+    let tree = sample_tree();\n+    assert_eq!(tree_weight_v1(tree), (1 + 2 + 3) + 4 + 5);\n+\n+    // If you uncomment this line below ...\n+    \n+    // assert_eq!(tree_weight_v1(tree), (1 + 2 + 3) + 4 + 5);\n+\n+    // ... you will get: error: use of moved value: `tree`\n+}\n+```\n+\n+This is *not* a consequence, however, of using `match`; it is rather\n+a consequence of the function signature that was chosen:\n+\n+```rust\n+fn tree_weight_v1(t: BinaryTree) -> i32 { 0 }\n+//                   ^~~~~~~~~~ this means this function takes ownership of `t`\n+```\n+\n+In fact, in Rust, `match` is designed to work quite well *without*\n+taking ownership. In particular, the input to `match` is an *[L-value][L_value]\n+expression*; this means that the input expression is evaluated to a\n+*memory location* where the value lives.\n+`match` works by doing this evaluation and then\n+inspecting the data at that memory location.\n+\n+(If the input expression is a variable name or a field/pointer\n+dereference, then the L-value is just the location of that variable or\n+field/memory.  If the input expression is a function call or other\n+operation that generates an unnamed temporary value, then it will be\n+conceptually stored in a temporary area, and that is the memory\n+location that `match` will inspect.)\n+\n+So, if we want a version of `tree_weight` that merely borrows a tree\n+rather than taking ownership of it, then we will need to make use of\n+this feature of Rust's `match`.\n+\n+```rust\n+/// Sum of values in all the nodes and leaves of `t`.\n+fn tree_weight_v2(t: &BinaryTree) -> i32 {\n+    //               ^~~~~~~~~~~ The `&` means we are *borrowing* the tree\n+    match *t {\n+        BinaryTree::Leaf(payload) => payload,\n+        BinaryTree::Node(ref left, payload, ref right) => {\n+            tree_weight_v2(left) + payload + tree_weight_v2(right)\n+        }\n+    }\n+}\n+\n+#[test]\n+fn tree_demo_2() {\n+    let tree = sample_tree();\n+    assert_eq!(tree_weight_v2(&tree), (1 + 2 + 3) + 4 + 5);\n+}\n+```\n+\n+The function `tree_weight_v2` looks very much like `tree_weight_v1`.\n+The only differences are: we take `t` as a borrowed reference (the `&`\n+in its type), we added a dereference `*t`, and,\n+importantly, we use `ref`-bindings for `left` and\n+`right` in the `Node` case.\n+\n+The dereference `*t`, interpreted as an L-value expression, is just\n+extracting the memory address where the `BinaryTree` is represented\n+(since the `t: &BinaryTree` is just a *reference* to that data in\n+memory). The `*t` here is not making a copy of the tree, nor moving it\n+to a new temporary location, because `match` is treating it as an\n+L-value.\n+\n+The only piece left is the `ref`-binding, which\n+is a crucial part of how destructuring bind of\n+L-values works.\n+\n+When matching a value of type `T`, an identifier pattern `i` will, on\n+a successful match, *move* the value out of the original input and\n+into `i`. Thus we can always conclude in such a case that `i` has type\n+`T`, or \"`i: T`\". (For some types `T`, known as *copyable* `T` or \"`T`\n+implements `Copy`\", the value will in fact be copied into `i` for such\n+identifier patterns; but in general types are not copyable; either\n+way, such bindings do mean that `i` has ownership of a value of type\n+`T`.)\n+\n+Thus, the bindings of `payload` in `tree_weight_v2` both have type\n+`i32`; the `i32` type implements `Copy`, so the weight is copied into\n+`payload` in both arms.\n+\n+However, when matching a value of type `T`, a `ref`-pattern `ref i`\n+will, on a successful match, merely *borrow* a reference into the\n+matched data. In other words, a successful `ref i` match of a value of\n+type `T` will imply that `i: &T`. Thus, in the `Node` arm of\n+`tree_weight_v2`, `left` will be reference the left-hand box (which","path":"_posts/2015-04-17-Enums-match-mutation-and-moves.md","position":548,"original_position":548,"commit_id":"9fbbcad6640c7cb1fdaa7754c582937cd94a338b","original_commit_id":"9fbbcad6640c7cb1fdaa7754c582937cd94a338b","user":{"login":"pnkfelix","id":173127,"avatar_url":"https://avatars.githubusercontent.com/u/173127?v=3","gravatar_id":"","url":"https://api.github.com/users/pnkfelix","html_url":"https://github.com/pnkfelix","followers_url":"https://api.github.com/users/pnkfelix/followers","following_url":"https://api.github.com/users/pnkfelix/following{/other_user}","gists_url":"https://api.github.com/users/pnkfelix/gists{/gist_id}","starred_url":"https://api.github.com/users/pnkfelix/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pnkfelix/subscriptions","organizations_url":"https://api.github.com/users/pnkfelix/orgs","repos_url":"https://api.github.com/users/pnkfelix/repos","events_url":"https://api.github.com/users/pnkfelix/events{/privacy}","received_events_url":"https://api.github.com/users/pnkfelix/received_events","type":"User","site_admin":false},"body":"Typo: will be reference ","created_at":"2015-04-17T07:43:58Z","updated_at":"2015-04-17T07:43:58Z","html_url":"https://github.com/rust-lang/blog.rust-lang.org/pull/38#discussion_r28575366","pull_request_url":"https://api.github.com/repos/rust-lang/blog.rust-lang.org/pulls/38","_links":{"self":{"href":"https://api.github.com/repos/rust-lang/blog.rust-lang.org/pulls/comments/28575366"},"html":{"href":"https://github.com/rust-lang/blog.rust-lang.org/pull/38#discussion_r28575366"},"pull_request":{"href":"https://api.github.com/repos/rust-lang/blog.rust-lang.org/pulls/38"}}},"pull_request":{"url":"https://api.github.com/repos/rust-lang/blog.rust-lang.org/pulls/38","id":32947653,"html_url":"https://github.com/rust-lang/blog.rust-lang.org/pull/38","diff_url":"https://github.com/rust-lang/blog.rust-lang.org/pull/38.diff","patch_url":"https://github.com/rust-lang/blog.rust-lang.org/pull/38.patch","issue_url":"https://api.github.com/repos/rust-lang/blog.rust-lang.org/issues/38","number":38,"state":"open","locked":false,"title":"second draft of enums post.","user":{"login":"pnkfelix","id":173127,"avatar_url":"https://avatars.githubusercontent.com/u/173127?v=3","gravatar_id":"","url":"https://api.github.com/users/pnkfelix","html_url":"https://github.com/pnkfelix","followers_url":"https://api.github.com/users/pnkfelix/followers","following_url":"https://api.github.com/users/pnkfelix/following{/other_user}","gists_url":"https://api.github.com/users/pnkfelix/gists{/gist_id}","starred_url":"https://api.github.com/users/pnkfelix/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pnkfelix/subscriptions","organizations_url":"https://api.github.com/users/pnkfelix/orgs","repos_url":"https://api.github.com/users/pnkfelix/repos","events_url":"https://api.github.com/users/pnkfelix/events{/privacy}","received_events_url":"https://api.github.com/users/pnkfelix/received_events","type":"User","site_admin":false},"body":"cc @nikomatsakis @aturon ","created_at":"2015-04-09T11:39:01Z","updated_at":"2015-04-17T07:43:58Z","closed_at":null,"merged_at":null,"merge_commit_sha":"6095afc7745a21aa15d23fc8929c7209c037a5d5","assignee":{"login":"nikomatsakis","id":155238,"avatar_url":"https://avatars.githubusercontent.com/u/155238?v=3","gravatar_id":"","url":"https://api.github.com/users/nikomatsakis","html_url":"https://github.com/nikomatsakis","followers_url":"https://api.github.com/users/nikomatsakis/followers","following_url":"https://api.github.com/users/nikomatsakis/following{/other_user}","gists_url":"https://api.github.com/users/nikomatsakis/gists{/gist_id}","starred_url":"https://api.github.com/users/nikomatsakis/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nikomatsakis/subscriptions","organizations_url":"https://api.github.com/users/nikomatsakis/orgs","repos_url":"https://api.github.com/users/nikomatsakis/repos","events_url":"https://api.github.com/users/nikomatsakis/events{/privacy}","received_events_url":"https://api.github.com/users/nikomatsakis/received_events","type":"User","site_admin":false},"milestone":null,"commits_url":"https://api.github.com/repos/rust-lang/blog.rust-lang.org/pulls/38/commits","review_comments_url":"https://api.github.com/repos/rust-lang/blog.rust-lang.org/pulls/38/comments","review_comment_url":"https://api.github.com/repos/rust-lang/blog.rust-lang.org/pulls/comments{/number}","comments_url":"https://api.github.com/repos/rust-lang/blog.rust-lang.org/issues/38/comments","statuses_url":"https://api.github.com/repos/rust-lang/blog.rust-lang.org/statuses/9fbbcad6640c7cb1fdaa7754c582937cd94a338b","head":{"label":"pnkfelix:fsk-enums-post","ref":"fsk-enums-post","sha":"9fbbcad6640c7cb1fdaa7754c582937cd94a338b","user":{"login":"pnkfelix","id":173127,"avatar_url":"https://avatars.githubusercontent.com/u/173127?v=3","gravatar_id":"","url":"https://api.github.com/users/pnkfelix","html_url":"https://github.com/pnkfelix","followers_url":"https://api.github.com/users/pnkfelix/followers","following_url":"https://api.github.com/users/pnkfelix/following{/other_user}","gists_url":"https://api.github.com/users/pnkfelix/gists{/gist_id}","starred_url":"https://api.github.com/users/pnkfelix/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pnkfelix/subscriptions","organizations_url":"https://api.github.com/users/pnkfelix/orgs","repos_url":"https://api.github.com/users/pnkfelix/repos","events_url":"https://api.github.com/users/pnkfelix/events{/privacy}","received_events_url":"https://api.github.com/users/pnkfelix/received_events","type":"User","site_admin":false},"repo":{"id":33618325,"name":"blog.rust-lang.org","full_name":"pnkfelix/blog.rust-lang.org","owner":{"login":"pnkfelix","id":173127,"avatar_url":"https://avatars.githubusercontent.com/u/173127?v=3","gravatar_id":"","url":"https://api.github.com/users/pnkfelix","html_url":"https://github.com/pnkfelix","followers_url":"https://api.github.com/users/pnkfelix/followers","following_url":"https://api.github.com/users/pnkfelix/following{/other_user}","gists_url":"https://api.github.com/users/pnkfelix/gists{/gist_id}","starred_url":"https://api.github.com/users/pnkfelix/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pnkfelix/subscriptions","organizations_url":"https://api.github.com/users/pnkfelix/orgs","repos_url":"https://api.github.com/users/pnkfelix/repos","events_url":"https://api.github.com/users/pnkfelix/events{/privacy}","received_events_url":"https://api.github.com/users/pnkfelix/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/pnkfelix/blog.rust-lang.org","description":"","fork":true,"url":"https://api.github.com/repos/pnkfelix/blog.rust-lang.org","forks_url":"https://api.github.com/repos/pnkfelix/blog.rust-lang.org/forks","keys_url":"https://api.github.com/repos/pnkfelix/blog.rust-lang.org/keys{/key_id}","collaborators_url":"https://api.github.com/repos/pnkfelix/blog.rust-lang.org/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/pnkfelix/blog.rust-lang.org/teams","hooks_url":"https://api.github.com/repos/pnkfelix/blog.rust-lang.org/hooks","issue_events_url":"https://api.github.com/repos/pnkfelix/blog.rust-lang.org/issues/events{/number}","events_url":"https://api.github.com/repos/pnkfelix/blog.rust-lang.org/events","assignees_url":"https://api.github.com/repos/pnkfelix/blog.rust-lang.org/assignees{/user}","branches_url":"https://api.github.com/repos/pnkfelix/blog.rust-lang.org/branches{/branch}","tags_url":"https://api.github.com/repos/pnkfelix/blog.rust-lang.org/tags","blobs_url":"https://api.github.com/repos/pnkfelix/blog.rust-lang.org/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/pnkfelix/blog.rust-lang.org/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/pnkfelix/blog.rust-lang.org/git/refs{/sha}","trees_url":"https://api.github.com/repos/pnkfelix/blog.rust-lang.org/git/trees{/sha}","statuses_url":"https://api.github.com/repos/pnkfelix/blog.rust-lang.org/statuses/{sha}","languages_url":"https://api.github.com/repos/pnkfelix/blog.rust-lang.org/languages","stargazers_url":"https://api.github.com/repos/pnkfelix/blog.rust-lang.org/stargazers","contributors_url":"https://api.github.com/repos/pnkfelix/blog.rust-lang.org/contributors","subscribers_url":"https://api.github.com/repos/pnkfelix/blog.rust-lang.org/subscribers","subscription_url":"https://api.github.com/repos/pnkfelix/blog.rust-lang.org/subscription","commits_url":"https://api.github.com/repos/pnkfelix/blog.rust-lang.org/commits{/sha}","git_commits_url":"https://api.github.com/repos/pnkfelix/blog.rust-lang.org/git/commits{/sha}","comments_url":"https://api.github.com/repos/pnkfelix/blog.rust-lang.org/comments{/number}","issue_comment_url":"https://api.github.com/repos/pnkfelix/blog.rust-lang.org/issues/comments{/number}","contents_url":"https://api.github.com/repos/pnkfelix/blog.rust-lang.org/contents/{+path}","compare_url":"https://api.github.com/repos/pnkfelix/blog.rust-lang.org/compare/{base}...{head}","merges_url":"https://api.github.com/repos/pnkfelix/blog.rust-lang.org/merges","archive_url":"https://api.github.com/repos/pnkfelix/blog.rust-lang.org/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/pnkfelix/blog.rust-lang.org/downloads","issues_url":"https://api.github.com/repos/pnkfelix/blog.rust-lang.org/issues{/number}","pulls_url":"https://api.github.com/repos/pnkfelix/blog.rust-lang.org/pulls{/number}","milestones_url":"https://api.github.com/repos/pnkfelix/blog.rust-lang.org/milestones{/number}","notifications_url":"https://api.github.com/repos/pnkfelix/blog.rust-lang.org/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/pnkfelix/blog.rust-lang.org/labels{/name}","releases_url":"https://api.github.com/repos/pnkfelix/blog.rust-lang.org/releases{/id}","created_at":"2015-04-08T16:23:56Z","updated_at":"2015-04-08T16:23:56Z","pushed_at":"2015-04-16T23:51:32Z","git_url":"git://github.com/pnkfelix/blog.rust-lang.org.git","ssh_url":"git@github.com:pnkfelix/blog.rust-lang.org.git","clone_url":"https://github.com/pnkfelix/blog.rust-lang.org.git","svn_url":"https://github.com/pnkfelix/blog.rust-lang.org","homepage":"http://blog.rust-lang.org","size":186,"stargazers_count":0,"watchers_count":0,"language":"CSS","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":true,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"gh-pages"}},"base":{"label":"rust-lang:gh-pages","ref":"gh-pages","sha":"7fda9199d11afdbaad4f6331aa6c359e407babdc","user":{"login":"rust-lang","id":5430905,"avatar_url":"https://avatars.githubusercontent.com/u/5430905?v=3","gravatar_id":"","url":"https://api.github.com/users/rust-lang","html_url":"https://github.com/rust-lang","followers_url":"https://api.github.com/users/rust-lang/followers","following_url":"https://api.github.com/users/rust-lang/following{/other_user}","gists_url":"https://api.github.com/users/rust-lang/gists{/gist_id}","starred_url":"https://api.github.com/users/rust-lang/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rust-lang/subscriptions","organizations_url":"https://api.github.com/users/rust-lang/orgs","repos_url":"https://api.github.com/users/rust-lang/repos","events_url":"https://api.github.com/users/rust-lang/events{/privacy}","received_events_url":"https://api.github.com/users/rust-lang/received_events","type":"Organization","site_admin":false},"repo":{"id":23805312,"name":"blog.rust-lang.org","full_name":"rust-lang/blog.rust-lang.org","owner":{"login":"rust-lang","id":5430905,"avatar_url":"https://avatars.githubusercontent.com/u/5430905?v=3","gravatar_id":"","url":"https://api.github.com/users/rust-lang","html_url":"https://github.com/rust-lang","followers_url":"https://api.github.com/users/rust-lang/followers","following_url":"https://api.github.com/users/rust-lang/following{/other_user}","gists_url":"https://api.github.com/users/rust-lang/gists{/gist_id}","starred_url":"https://api.github.com/users/rust-lang/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rust-lang/subscriptions","organizations_url":"https://api.github.com/users/rust-lang/orgs","repos_url":"https://api.github.com/users/rust-lang/repos","events_url":"https://api.github.com/users/rust-lang/events{/privacy}","received_events_url":"https://api.github.com/users/rust-lang/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/rust-lang/blog.rust-lang.org","description":"","fork":false,"url":"https://api.github.com/repos/rust-lang/blog.rust-lang.org","forks_url":"https://api.github.com/repos/rust-lang/blog.rust-lang.org/forks","keys_url":"https://api.github.com/repos/rust-lang/blog.rust-lang.org/keys{/key_id}","collaborators_url":"https://api.github.com/repos/rust-lang/blog.rust-lang.org/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/rust-lang/blog.rust-lang.org/teams","hooks_url":"https://api.github.com/repos/rust-lang/blog.rust-lang.org/hooks","issue_events_url":"https://api.github.com/repos/rust-lang/blog.rust-lang.org/issues/events{/number}","events_url":"https://api.github.com/repos/rust-lang/blog.rust-lang.org/events","assignees_url":"https://api.github.com/repos/rust-lang/blog.rust-lang.org/assignees{/user}","branches_url":"https://api.github.com/repos/rust-lang/blog.rust-lang.org/branches{/branch}","tags_url":"https://api.github.com/repos/rust-lang/blog.rust-lang.org/tags","blobs_url":"https://api.github.com/repos/rust-lang/blog.rust-lang.org/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/rust-lang/blog.rust-lang.org/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/rust-lang/blog.rust-lang.org/git/refs{/sha}","trees_url":"https://api.github.com/repos/rust-lang/blog.rust-lang.org/git/trees{/sha}","statuses_url":"https://api.github.com/repos/rust-lang/blog.rust-lang.org/statuses/{sha}","languages_url":"https://api.github.com/repos/rust-lang/blog.rust-lang.org/languages","stargazers_url":"https://api.github.com/repos/rust-lang/blog.rust-lang.org/stargazers","contributors_url":"https://api.github.com/repos/rust-lang/blog.rust-lang.org/contributors","subscribers_url":"https://api.github.com/repos/rust-lang/blog.rust-lang.org/subscribers","subscription_url":"https://api.github.com/repos/rust-lang/blog.rust-lang.org/subscription","commits_url":"https://api.github.com/repos/rust-lang/blog.rust-lang.org/commits{/sha}","git_commits_url":"https://api.github.com/repos/rust-lang/blog.rust-lang.org/git/commits{/sha}","comments_url":"https://api.github.com/repos/rust-lang/blog.rust-lang.org/comments{/number}","issue_comment_url":"https://api.github.com/repos/rust-lang/blog.rust-lang.org/issues/comments{/number}","contents_url":"https://api.github.com/repos/rust-lang/blog.rust-lang.org/contents/{+path}","compare_url":"https://api.github.com/repos/rust-lang/blog.rust-lang.org/compare/{base}...{head}","merges_url":"https://api.github.com/repos/rust-lang/blog.rust-lang.org/merges","archive_url":"https://api.github.com/repos/rust-lang/blog.rust-lang.org/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/rust-lang/blog.rust-lang.org/downloads","issues_url":"https://api.github.com/repos/rust-lang/blog.rust-lang.org/issues{/number}","pulls_url":"https://api.github.com/repos/rust-lang/blog.rust-lang.org/pulls{/number}","milestones_url":"https://api.github.com/repos/rust-lang/blog.rust-lang.org/milestones{/number}","notifications_url":"https://api.github.com/repos/rust-lang/blog.rust-lang.org/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/rust-lang/blog.rust-lang.org/labels{/name}","releases_url":"https://api.github.com/repos/rust-lang/blog.rust-lang.org/releases{/id}","created_at":"2014-09-08T19:38:05Z","updated_at":"2015-04-16T14:33:15Z","pushed_at":"2015-04-16T23:51:33Z","git_url":"git://github.com/rust-lang/blog.rust-lang.org.git","ssh_url":"git@github.com:rust-lang/blog.rust-lang.org.git","clone_url":"https://github.com/rust-lang/blog.rust-lang.org.git","svn_url":"https://github.com/rust-lang/blog.rust-lang.org","homepage":"http://blog.rust-lang.org","size":895,"stargazers_count":5,"watchers_count":5,"language":"CSS","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":true,"forks_count":18,"mirror_url":null,"open_issues_count":1,"forks":18,"open_issues":1,"watchers":5,"default_branch":"gh-pages"}},"_links":{"self":{"href":"https://api.github.com/repos/rust-lang/blog.rust-lang.org/pulls/38"},"html":{"href":"https://github.com/rust-lang/blog.rust-lang.org/pull/38"},"issue":{"href":"https://api.github.com/repos/rust-lang/blog.rust-lang.org/issues/38"},"comments":{"href":"https://api.github.com/repos/rust-lang/blog.rust-lang.org/issues/38/comments"},"review_comments":{"href":"https://api.github.com/repos/rust-lang/blog.rust-lang.org/pulls/38/comments"},"review_comment":{"href":"https://api.github.com/repos/rust-lang/blog.rust-lang.org/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/rust-lang/blog.rust-lang.org/pulls/38/commits"},"statuses":{"href":"https://api.github.com/repos/rust-lang/blog.rust-lang.org/statuses/9fbbcad6640c7cb1fdaa7754c582937cd94a338b"}}}}