{"action":"created","comment":{"url":"https://api.github.com/repos/rust-lang/rust/pulls/comments/91079029","pull_request_review_id":11588512,"id":91079029,"diff_hunk":"@@ -1305,213 +1304,290 @@ impl<T: Clone> ToOwned for [T] {\n // Sorting\n ////////////////////////////////////////////////////////////////////////////////\n \n-fn insertion_sort<T, F>(v: &mut [T], mut compare: F)\n+/// Inserts `v[0]` into pre-sorted sequence `v[1..]` so that whole `v[..]` becomes sorted.\n+///\n+/// This is the integral subroutine of insertion sort.\n+fn insert_head<T, F>(v: &mut [T], compare: &mut F)\n     where F: FnMut(&T, &T) -> Ordering\n {\n-    let len = v.len() as isize;\n-    let buf_v = v.as_mut_ptr();\n-\n-    // 1 <= i < len;\n-    for i in 1..len {\n-        // j satisfies: 0 <= j <= i;\n-        let mut j = i;\n+    if v.len() >= 2 && compare(&v[0], &v[1]) == Greater {\n         unsafe {\n-            // `i` is in bounds.\n-            let read_ptr = buf_v.offset(i) as *const T;\n-\n-            // find where to insert, we need to do strict <,\n-            // rather than <=, to maintain stability.\n-\n-            // 0 <= j - 1 < len, so .offset(j - 1) is in bounds.\n-            while j > 0 && compare(&*read_ptr, &*buf_v.offset(j - 1)) == Less {\n-                j -= 1;\n+            // A common method of doing insertion sort is swapping adjacent elements until the\n+            // first one gets to it's destination. Swapping is slow whenever elements are big\n+            // structures and therefore costly to copy.\n+            //\n+            // We opt for a different approach: copy the first element into a temporary variable,\n+            // shift lesser elements succeeding it, and then copy it back into the remaining hole.\n+            let mut tmp = NoDrop { value: ptr::read(&v[0]) };\n+\n+            // Panic safety:\n+            //\n+            // The guard holds the intermediate state of the insertion process. It knows where the\n+            // hole currently is. If `compare` panics at any point during the process shifting, the\n+            // guard will get dropped, copy the temporary variable into the hole, and thus ensure\n+            // that `v` still holds every object it initially held exactly once.\n+            let mut guard = Guard {\n+                src: &mut tmp.value,\n+                dst: &mut v[1],\n+            };\n+            ptr::copy_nonoverlapping(&v[1], &mut v[0], 1);\n+\n+            for i in 2..v.len() {\n+                if compare(&tmp.value, &v[i]) != Greater {\n+                    break;\n+                }\n+                ptr::copy_nonoverlapping(&v[i], &mut v[i - 1], 1);\n+                guard.dst = &mut v[i];\n             }\n+        }\n+    }\n \n-            // shift everything to the right, to make space to\n-            // insert this value.\n+    // Holds a value, but never drops it.\n+    #[allow(unions_with_drop_fields)]\n+    union NoDrop<T> {\n+        value: T\n+    }\n \n-            // j + 1 could be `len` (for the last `i`), but in\n-            // that case, `i == j` so we don't copy. The\n-            // `.offset(j)` is always in bounds.\n+    // When dropped, copies from `src` into `dst`.\n+    struct Guard<T> {\n+        src: *mut T,\n+        dst: *mut T,\n+    }\n \n-            if i != j {\n-                let tmp = ptr::read(read_ptr);\n-                ptr::copy(&*buf_v.offset(j), buf_v.offset(j + 1), (i - j) as usize);\n-                ptr::copy_nonoverlapping(&tmp, buf_v.offset(j), 1);\n-                mem::forget(tmp);\n-            }\n+    impl<T> Drop for Guard<T> {\n+        fn drop(&mut self) {\n+            unsafe { ptr::copy_nonoverlapping(self.src, self.dst, 1); }\n         }\n     }\n }\n \n-fn merge_sort<T, F>(v: &mut [T], mut compare: F)\n+/// Merges non-decreasing runs `v[..mid]` and `v[mid..]` using `buf` as temporary storage, and\n+/// stores the result into `v[..]`.\n+///\n+/// # Safety\n+///\n+/// The two slices must be non-empty and `mid` must be in bounds. Buffer `buf` must be long enough\n+/// to hold a copy of the shorter slice.\n+unsafe fn merge<T, F>(v: &mut [T], mid: usize, buf: *mut T, compare: &mut F)\n     where F: FnMut(&T, &T) -> Ordering\n {\n-    // warning: this wildly uses unsafe.\n-    const BASE_INSERTION: usize = 32;\n-    const LARGE_INSERTION: usize = 16;\n-\n-    // FIXME #12092: smaller insertion runs seems to make sorting\n-    // vectors of large elements a little faster on some platforms,\n-    // but hasn't been tested/tuned extensively\n-    let insertion = if size_of::<T>() <= 16 {\n-        BASE_INSERTION\n+    let len = v.len();\n+    let v = v.as_mut_ptr();\n+    let v_mid = v.offset(mid as isize);\n+    let v_end = v.offset(len as isize);\n+\n+    // The merge process first copies the shorter run into `buf`. Then it traces the newly copied\n+    // run and the longer run forwards (or backwards), comparing their next unconsumed elements and\n+    // copying the lesser (or greater) one into `v`.\n+    //\n+    // As soon as the shorter run is fully consumed, the process is done. If the longer run gets\n+    // consumed first, then we must copy the remains of the shorter run into `v` at the place where\n+    // we left off. The guard will do that as soon as it's dropped.\n+    //\n+    // Panic safety:\n+    //\n+    // The guard holds the intermediate state of the merge process. It knows about the still\n+    // unconsumed range in `buf` and the position in `v` where the next object is about to be\n+    // placed. If `compare` panics at any point during the process, the guard will get dropped,\n+    // copy the range back into `v`, and thus ensure that `v` still holds every object it\n+    // initially held exactly once.\n+    let mut guard;\n+\n+    if mid <= len - mid {\n+        // The left run is shorter.\n+        ptr::copy_nonoverlapping(v, buf, mid);\n+        guard = Guard {\n+            start: buf,\n+            end: buf.offset(mid as isize),\n+            dst: v,\n+        };\n+\n+        let left = &mut guard.start;\n+        let mut right = v_mid;\n+        let out = &mut guard.dst;\n+\n+        while *left < guard.end && right < v_end {\n+            // Consume the lesser side.\n+            // If equal, prefer the left run to maintain stability.\n+            let to_copy = if compare(&**left, &*right) == Greater {\n+                forward(&mut right)\n+            } else {\n+                forward(left)\n+            };\n+            ptr::copy_nonoverlapping(to_copy, forward(out), 1);\n+        }\n     } else {\n-        LARGE_INSERTION\n-    };\n+        // The right run is shorter.\n+        ptr::copy_nonoverlapping(v_mid, buf, len - mid);\n+        guard = Guard {\n+            start: buf,\n+            end: buf.offset((len - mid) as isize),\n+            dst: v_mid,\n+        };\n+\n+        let left = &mut guard.dst;\n+        let right = &mut guard.end;\n+        let mut out = v_end;\n+\n+        while v < *left && buf < *right {\n+            // Consume the greater side.\n+            // If equal, prefer the right run to maintain stability.\n+            let to_copy = if compare(&*left.offset(-1), &*right.offset(-1)) == Greater {\n+                backward(left)\n+            } else {\n+                backward(right)\n+            };\n+            ptr::copy_nonoverlapping(to_copy, backward(&mut out), 1);\n+        }\n+    }\n ","path":"src/libcollections/slice.rs","position":217,"original_position":217,"commit_id":"1814334b05490a0cbbb132e133a13875dbe5a55f","original_commit_id":"1814334b05490a0cbbb132e133a13875dbe5a55f","user":{"login":"bluss","id":3209739,"avatar_url":"https://avatars.githubusercontent.com/u/3209739?v=3","gravatar_id":"","url":"https://api.github.com/users/bluss","html_url":"https://github.com/bluss","followers_url":"https://api.github.com/users/bluss/followers","following_url":"https://api.github.com/users/bluss/following{/other_user}","gists_url":"https://api.github.com/users/bluss/gists{/gist_id}","starred_url":"https://api.github.com/users/bluss/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bluss/subscriptions","organizations_url":"https://api.github.com/users/bluss/orgs","repos_url":"https://api.github.com/users/bluss/repos","events_url":"https://api.github.com/users/bluss/events{/privacy}","received_events_url":"https://api.github.com/users/bluss/received_events","type":"User","site_admin":false},"body":"The same here: a comment reminding that `guard` is dropped and it copies the range into place.","created_at":"2016-12-06T13:43:36Z","updated_at":"2016-12-06T13:44:27Z","html_url":"https://github.com/rust-lang/rust/pull/38192#discussion_r91079029","pull_request_url":"https://api.github.com/repos/rust-lang/rust/pulls/38192","_links":{"self":{"href":"https://api.github.com/repos/rust-lang/rust/pulls/comments/91079029"},"html":{"href":"https://github.com/rust-lang/rust/pull/38192#discussion_r91079029"},"pull_request":{"href":"https://api.github.com/repos/rust-lang/rust/pulls/38192"}}},"pull_request":{"url":"https://api.github.com/repos/rust-lang/rust/pulls/38192","id":96713733,"html_url":"https://github.com/rust-lang/rust/pull/38192","diff_url":"https://github.com/rust-lang/rust/pull/38192.diff","patch_url":"https://github.com/rust-lang/rust/pull/38192.patch","issue_url":"https://api.github.com/repos/rust-lang/rust/issues/38192","number":38192,"state":"open","locked":false,"title":"Implement a faster sort algorithm","user":{"login":"stjepang","id":1303843,"avatar_url":"https://avatars.githubusercontent.com/u/1303843?v=3","gravatar_id":"","url":"https://api.github.com/users/stjepang","html_url":"https://github.com/stjepang","followers_url":"https://api.github.com/users/stjepang/followers","following_url":"https://api.github.com/users/stjepang/following{/other_user}","gists_url":"https://api.github.com/users/stjepang/gists{/gist_id}","starred_url":"https://api.github.com/users/stjepang/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/stjepang/subscriptions","organizations_url":"https://api.github.com/users/stjepang/orgs","repos_url":"https://api.github.com/users/stjepang/repos","events_url":"https://api.github.com/users/stjepang/events{/privacy}","received_events_url":"https://api.github.com/users/stjepang/received_events","type":"User","site_admin":false},"body":"Hi everyone, this is my first PR.\r\n\r\nI've made some changes to the standard sort algorithm, starting out with a few tweaks here and there, but in the end this endeavour became a complete rewrite of it.\r\n\r\n#### Summary\r\n\r\nChanges:\r\n\r\n* Improved performance, especially on partially sorted inputs.\r\n* Performs less comparisons on both random and partially sorted inputs.\r\n* Decreased the size of temporary memory: the new sort allocates 4x less.\r\n\r\nBenchmark:\r\n\r\n```\r\n name                                        out1 ns/iter          out2 ns/iter          diff ns/iter   diff %\r\n slice::bench::sort_large_ascending          85,323 (937 MB/s)     8,970 (8918 MB/s)          -76,353  -89.49%\r\n slice::bench::sort_large_big_ascending      2,135,297 (599 MB/s)  355,955 (3595 MB/s)     -1,779,342  -83.33%\r\n slice::bench::sort_large_big_descending     2,266,402 (564 MB/s)  416,479 (3073 MB/s)     -1,849,923  -81.62%\r\n slice::bench::sort_large_big_random         3,053,031 (419 MB/s)  1,921,389 (666 MB/s)    -1,131,642  -37.07%\r\n slice::bench::sort_large_descending         313,181 (255 MB/s)    14,725 (5432 MB/s)        -298,456  -95.30%\r\n slice::bench::sort_large_mostly_ascending   287,706 (278 MB/s)    243,204 (328 MB/s)         -44,502  -15.47%\r\n slice::bench::sort_large_mostly_descending  415,078 (192 MB/s)    271,028 (295 MB/s)        -144,050  -34.70%\r\n slice::bench::sort_large_random             545,872 (146 MB/s)    521,559 (153 MB/s)         -24,313   -4.45%\r\n slice::bench::sort_large_random_expensive   30,321,770 (2 MB/s)   23,533,735 (3 MB/s)     -6,788,035  -22.39%\r\n slice::bench::sort_medium_ascending         616 (1298 MB/s)       155 (5161 MB/s)               -461  -74.84%\r\n slice::bench::sort_medium_descending        1,952 (409 MB/s)      202 (3960 MB/s)             -1,750  -89.65%\r\n slice::bench::sort_medium_random            3,646 (219 MB/s)      3,421 (233 MB/s)              -225   -6.17%\r\n slice::bench::sort_small_ascending          39 (2051 MB/s)        34 (2352 MB/s)                  -5  -12.82%\r\n slice::bench::sort_small_big_ascending      96 (13333 MB/s)       96 (13333 MB/s)                  0    0.00%\r\n slice::bench::sort_small_big_descending     248 (5161 MB/s)       243 (5267 MB/s)                 -5   -2.02%\r\n slice::bench::sort_small_big_random         501 (2554 MB/s)       490 (2612 MB/s)                -11   -2.20%\r\n slice::bench::sort_small_descending         95 (842 MB/s)         63 (1269 MB/s)                 -32  -33.68%\r\n slice::bench::sort_small_random             372 (215 MB/s)        354 (225 MB/s)                 -18   -4.84%\r\n```\r\n\r\n#### Background\r\n\r\nFirst, let me just do a quick brain dump to discuss what I learned along the way.\r\n\r\nThe official documentation says that the standard sort in Rust is a stable sort. This constraint is thus set in stone and immediately rules out many popular sorting algorithms. Essentially, the only algorithms we might even take into consideration are:\r\n\r\n1. [Merge sort](https://en.wikipedia.org/wiki/Merge_sort)\r\n2. [Block sort](https://en.wikipedia.org/wiki/Block_sort) (famous implementations are [WikiSort](https://github.com/BonzaiThePenguin/WikiSort) and [GrailSort](https://github.com/Mrrl/GrailSort))\r\n3. [TimSort](https://en.wikipedia.org/wiki/Timsort)\r\n\r\nActually, all of those are just merge sort flavors. :) The current standard sort in Rust is a simple iterative merge sort. It has three problems. First, it's slow on partially sorted inputs (even though #29675 helped quite a bit). Second, it always makes around `log(n)` iterations copying the entire array between buffers, no matter what. Third, it allocates huge amounts of temporary memory (a buffer of size `2*n`, where `n` is the size of input).\r\n\r\nThe problem of auxilliary memory allocation is a tough one. Ideally, it would be best for our sort\r\nto allocate `O(1)` additional memory. This is what block sort (and it's variants) does. However, it's often very complicated (look at [this](https://github.com/BonzaiThePenguin/WikiSort/blob/master/WikiSort.cpp)) and even then performs rather poorly. The author of WikiSort claims good performance, but that must be taken with a grain of salt. It performs well in comparison to `std::stable_sort` in C++. It can even beat `std::sort` on partially sorted inputs, but on random inputs it's always far worse. My rule of thumb is: high performance, low memory overhead, stability - choose two.\r\n\r\nTimSort is another option. It allocates a buffer of size `n/2`, which is not great, but acceptable. Performs extremelly well on partially sorted inputs. However, it seems pretty much all implementations suck on random inputs. I benchmarked implementations in [Rust](https://github.com/notriddle/rust-timsort), [C++](https://github.com/gfx/cpp-TimSort), and [D](https://github.com/dlang/phobos/blob/fd518eb310a9494cccf28c54892542b052c49669/std/algorithm/sorting.d#L2062). The results were a bit disappointing. It seems bad performance is due to complex galloping procedures in hot loops. Galloping noticeably improves performance on partially sorted inputs, but worsens it on random ones.\r\n\r\n#### The new algorithm\r\n\r\nChoosing the best algorithm is not easy. Plain merge sort is bad on partially sorted inputs. TimSort is\r\nbad on random inputs and block sort is even worse. However, if we take the main ideas from TimSort\r\n(intelligent merging stragegy of sorted runs) and drop galloping, then we'll have great performance\r\non random inputs and it won't be bad on partially sorted inputs either.\r\n\r\nThat is exactly what this new algorithm does. I can't call it TimSort, since it steals just a few of it's ideas. Complete TimSort would be a much more complex and elaborate implementation. In case we\r\nin the future figure out how to incorporate more of it's ideas into this implementation without crippling performance on random inputs, it's going to be very easy to extend.\r\nI also did several other minor improvements, like reworked insertion sort to make it faster.\r\n\r\nThere are also new, more thorough benchmarks and panic safety tests.\r\n\r\nThe final code is not terribly complex and has less unsafe code than I anticipated, but there's still plenty of it that should be carefully reviewed. I did my best at documenting non-obvious code.\r\n\r\nI'd like to notify several people of this PR, since they might be interested and have useful insights:\r\n\r\n1. @huonw because he wrote the [original merge sort](https://github.com/rust-lang/rust/pull/11064).\r\n2. @alexcrichton because he was involved in multiple discussions of it.\r\n3. @veddan because he wrote [introsort](https://github.com/veddan/rust-introsort) in Rust.\r\n4. @notriddle because he wrote [TimSort](https://github.com/notriddle/rust-timsort) in Rust.\r\n5. @bluss because he had an attempt at writing WikiSort in Rust.\r\n6. @gnzlbg, @rkruppe, and @mark-i-m because they were involved in discussion #36318.\r\n\r\n**P.S.** [quickersort](https://github.com/notriddle/quickersort) describes itself as being universally [faster](https://github.com/notriddle/quickersort/blob/master/perf.txt) than the standard sort, which is true. However, if this PR gets merged, things might [change](https://gist.github.com/stjepang/b9f0c3eaa0e1f1280b61b963dae19a30) a bit. ;)\r\n","created_at":"2016-12-06T12:19:30Z","updated_at":"2016-12-06T13:44:27Z","closed_at":null,"merged_at":null,"merge_commit_sha":"21aa17855dfd4b9644b0163c8229fc37f6f1e264","assignee":null,"assignees":[],"milestone":null,"commits_url":"https://api.github.com/repos/rust-lang/rust/pulls/38192/commits","review_comments_url":"https://api.github.com/repos/rust-lang/rust/pulls/38192/comments","review_comment_url":"https://api.github.com/repos/rust-lang/rust/pulls/comments{/number}","comments_url":"https://api.github.com/repos/rust-lang/rust/issues/38192/comments","statuses_url":"https://api.github.com/repos/rust-lang/rust/statuses/1814334b05490a0cbbb132e133a13875dbe5a55f","head":{"label":"stjepang:faster-sort-algorithm","ref":"faster-sort-algorithm","sha":"1814334b05490a0cbbb132e133a13875dbe5a55f","user":{"login":"stjepang","id":1303843,"avatar_url":"https://avatars.githubusercontent.com/u/1303843?v=3","gravatar_id":"","url":"https://api.github.com/users/stjepang","html_url":"https://github.com/stjepang","followers_url":"https://api.github.com/users/stjepang/followers","following_url":"https://api.github.com/users/stjepang/following{/other_user}","gists_url":"https://api.github.com/users/stjepang/gists{/gist_id}","starred_url":"https://api.github.com/users/stjepang/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/stjepang/subscriptions","organizations_url":"https://api.github.com/users/stjepang/orgs","repos_url":"https://api.github.com/users/stjepang/repos","events_url":"https://api.github.com/users/stjepang/events{/privacy}","received_events_url":"https://api.github.com/users/stjepang/received_events","type":"User","site_admin":false},"repo":{"id":74284409,"name":"rust","full_name":"stjepang/rust","owner":{"login":"stjepang","id":1303843,"avatar_url":"https://avatars.githubusercontent.com/u/1303843?v=3","gravatar_id":"","url":"https://api.github.com/users/stjepang","html_url":"https://github.com/stjepang","followers_url":"https://api.github.com/users/stjepang/followers","following_url":"https://api.github.com/users/stjepang/following{/other_user}","gists_url":"https://api.github.com/users/stjepang/gists{/gist_id}","starred_url":"https://api.github.com/users/stjepang/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/stjepang/subscriptions","organizations_url":"https://api.github.com/users/stjepang/orgs","repos_url":"https://api.github.com/users/stjepang/repos","events_url":"https://api.github.com/users/stjepang/events{/privacy}","received_events_url":"https://api.github.com/users/stjepang/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/stjepang/rust","description":"A safe, concurrent, practical language.","fork":true,"url":"https://api.github.com/repos/stjepang/rust","forks_url":"https://api.github.com/repos/stjepang/rust/forks","keys_url":"https://api.github.com/repos/stjepang/rust/keys{/key_id}","collaborators_url":"https://api.github.com/repos/stjepang/rust/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/stjepang/rust/teams","hooks_url":"https://api.github.com/repos/stjepang/rust/hooks","issue_events_url":"https://api.github.com/repos/stjepang/rust/issues/events{/number}","events_url":"https://api.github.com/repos/stjepang/rust/events","assignees_url":"https://api.github.com/repos/stjepang/rust/assignees{/user}","branches_url":"https://api.github.com/repos/stjepang/rust/branches{/branch}","tags_url":"https://api.github.com/repos/stjepang/rust/tags","blobs_url":"https://api.github.com/repos/stjepang/rust/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/stjepang/rust/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/stjepang/rust/git/refs{/sha}","trees_url":"https://api.github.com/repos/stjepang/rust/git/trees{/sha}","statuses_url":"https://api.github.com/repos/stjepang/rust/statuses/{sha}","languages_url":"https://api.github.com/repos/stjepang/rust/languages","stargazers_url":"https://api.github.com/repos/stjepang/rust/stargazers","contributors_url":"https://api.github.com/repos/stjepang/rust/contributors","subscribers_url":"https://api.github.com/repos/stjepang/rust/subscribers","subscription_url":"https://api.github.com/repos/stjepang/rust/subscription","commits_url":"https://api.github.com/repos/stjepang/rust/commits{/sha}","git_commits_url":"https://api.github.com/repos/stjepang/rust/git/commits{/sha}","comments_url":"https://api.github.com/repos/stjepang/rust/comments{/number}","issue_comment_url":"https://api.github.com/repos/stjepang/rust/issues/comments{/number}","contents_url":"https://api.github.com/repos/stjepang/rust/contents/{+path}","compare_url":"https://api.github.com/repos/stjepang/rust/compare/{base}...{head}","merges_url":"https://api.github.com/repos/stjepang/rust/merges","archive_url":"https://api.github.com/repos/stjepang/rust/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/stjepang/rust/downloads","issues_url":"https://api.github.com/repos/stjepang/rust/issues{/number}","pulls_url":"https://api.github.com/repos/stjepang/rust/pulls{/number}","milestones_url":"https://api.github.com/repos/stjepang/rust/milestones{/number}","notifications_url":"https://api.github.com/repos/stjepang/rust/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/stjepang/rust/labels{/name}","releases_url":"https://api.github.com/repos/stjepang/rust/releases{/id}","deployments_url":"https://api.github.com/repos/stjepang/rust/deployments","created_at":"2016-11-20T15:40:43Z","updated_at":"2016-11-20T15:41:05Z","pushed_at":"2016-12-06T13:13:46Z","git_url":"git://github.com/stjepang/rust.git","ssh_url":"git@github.com:stjepang/rust.git","clone_url":"https://github.com/stjepang/rust.git","svn_url":"https://github.com/stjepang/rust","homepage":"https://www.rust-lang.org","size":312498,"stargazers_count":0,"watchers_count":0,"language":"Rust","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"rust-lang:master","ref":"master","sha":"ff261d3a6b5964e1e3744d055238de624afc5d76","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":724712,"name":"rust","full_name":"rust-lang/rust","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/rust","description":"A safe, concurrent, practical language.","fork":false,"url":"https://api.github.com/repos/rust-lang/rust","forks_url":"https://api.github.com/repos/rust-lang/rust/forks","keys_url":"https://api.github.com/repos/rust-lang/rust/keys{/key_id}","collaborators_url":"https://api.github.com/repos/rust-lang/rust/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/rust-lang/rust/teams","hooks_url":"https://api.github.com/repos/rust-lang/rust/hooks","issue_events_url":"https://api.github.com/repos/rust-lang/rust/issues/events{/number}","events_url":"https://api.github.com/repos/rust-lang/rust/events","assignees_url":"https://api.github.com/repos/rust-lang/rust/assignees{/user}","branches_url":"https://api.github.com/repos/rust-lang/rust/branches{/branch}","tags_url":"https://api.github.com/repos/rust-lang/rust/tags","blobs_url":"https://api.github.com/repos/rust-lang/rust/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/rust-lang/rust/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/rust-lang/rust/git/refs{/sha}","trees_url":"https://api.github.com/repos/rust-lang/rust/git/trees{/sha}","statuses_url":"https://api.github.com/repos/rust-lang/rust/statuses/{sha}","languages_url":"https://api.github.com/repos/rust-lang/rust/languages","stargazers_url":"https://api.github.com/repos/rust-lang/rust/stargazers","contributors_url":"https://api.github.com/repos/rust-lang/rust/contributors","subscribers_url":"https://api.github.com/repos/rust-lang/rust/subscribers","subscription_url":"https://api.github.com/repos/rust-lang/rust/subscription","commits_url":"https://api.github.com/repos/rust-lang/rust/commits{/sha}","git_commits_url":"https://api.github.com/repos/rust-lang/rust/git/commits{/sha}","comments_url":"https://api.github.com/repos/rust-lang/rust/comments{/number}","issue_comment_url":"https://api.github.com/repos/rust-lang/rust/issues/comments{/number}","contents_url":"https://api.github.com/repos/rust-lang/rust/contents/{+path}","compare_url":"https://api.github.com/repos/rust-lang/rust/compare/{base}...{head}","merges_url":"https://api.github.com/repos/rust-lang/rust/merges","archive_url":"https://api.github.com/repos/rust-lang/rust/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/rust-lang/rust/downloads","issues_url":"https://api.github.com/repos/rust-lang/rust/issues{/number}","pulls_url":"https://api.github.com/repos/rust-lang/rust/pulls{/number}","milestones_url":"https://api.github.com/repos/rust-lang/rust/milestones{/number}","notifications_url":"https://api.github.com/repos/rust-lang/rust/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/rust-lang/rust/labels{/name}","releases_url":"https://api.github.com/repos/rust-lang/rust/releases{/id}","deployments_url":"https://api.github.com/repos/rust-lang/rust/deployments","created_at":"2010-06-16T20:39:03Z","updated_at":"2016-12-06T11:28:57Z","pushed_at":"2016-12-06T13:13:48Z","git_url":"git://github.com/rust-lang/rust.git","ssh_url":"git@github.com:rust-lang/rust.git","clone_url":"https://github.com/rust-lang/rust.git","svn_url":"https://github.com/rust-lang/rust","homepage":"https://www.rust-lang.org","size":528697,"stargazers_count":19251,"watchers_count":19251,"language":"Rust","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":3700,"mirror_url":null,"open_issues_count":2935,"forks":3700,"open_issues":2935,"watchers":19251,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/rust-lang/rust/pulls/38192"},"html":{"href":"https://github.com/rust-lang/rust/pull/38192"},"issue":{"href":"https://api.github.com/repos/rust-lang/rust/issues/38192"},"comments":{"href":"https://api.github.com/repos/rust-lang/rust/issues/38192/comments"},"review_comments":{"href":"https://api.github.com/repos/rust-lang/rust/pulls/38192/comments"},"review_comment":{"href":"https://api.github.com/repos/rust-lang/rust/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/rust-lang/rust/pulls/38192/commits"},"statuses":{"href":"https://api.github.com/repos/rust-lang/rust/statuses/1814334b05490a0cbbb132e133a13875dbe5a55f"}}}}
{"actor":{"display_login":"bluss"}}