diff --git a/gc/Cargo.toml b/gc/Cargo.toml
index e3f81a0..c00de24 100644
--- a/gc/Cargo.toml
+++ b/gc/Cargo.toml
@@ -7,6 +7,7 @@ repository = "https://github.com/Manishearth/rust-gc"
 readme = "../README.md"
 license = "MPL-2.0"
 keywords = ["garbage", "plugin", "memory"]
+edition = "2018"
 
 [features]
 nightly = []
diff --git a/gc/benches/alloc_in_a_loop.rs b/gc/benches/alloc_in_a_loop.rs
index 2b9611d..af43004 100644
--- a/gc/benches/alloc_in_a_loop.rs
+++ b/gc/benches/alloc_in_a_loop.rs
@@ -1,7 +1,6 @@
 #![feature(test)]
 
 extern crate test;
-extern crate gc;
 
 const THING: u64 = 0;
 
diff --git a/gc/src/gc.rs b/gc/src/gc.rs
index 91c2d1c..b97234e 100644
--- a/gc/src/gc.rs
+++ b/gc/src/gc.rs
@@ -1,7 +1,7 @@
 use std::cell::{Cell, RefCell};
 use std::mem;
 use std::ptr::NonNull;
-use trace::{Finalize, Trace};
+use crate::trace::{Finalize, Trace};
 
 
 const INITIAL_THRESHOLD: usize = 100;
@@ -15,7 +15,7 @@ const USED_SPACE_RATIO: f64 = 0.7;
 struct GcState {
     bytes_allocated: usize,
     threshold: usize,
-    boxes_start: Option<NonNull<GcBox<Trace>>>,
+    boxes_start: Option<NonNull<GcBox<dyn Trace>>>,
 }
 
 impl Drop for GcState {
@@ -38,8 +38,8 @@ impl Drop for GcState {
     }
 }
 
-/// Whether or not the thread is currently in the sweep phase of garbage collection.
-/// During this phase, attempts to dereference a `Gc<T>` pointer will trigger a panic.
+// Whether or not the thread is currently in the sweep phase of garbage collection.
+// During this phase, attempts to dereference a `Gc<T>` pointer will trigger a panic.
 thread_local!(pub static GC_DROPPING: Cell<bool> = Cell::new(false));
 struct DropGuard;
 impl DropGuard {
@@ -57,23 +57,23 @@ pub fn finalizer_safe() -> bool {
     GC_DROPPING.with(|dropping| !dropping.get())
 }
 
-/// The garbage collector's internal state.
+// The garbage collector's internal state.
 thread_local!(static GC_STATE: RefCell<GcState> = RefCell::new(GcState {
     bytes_allocated: 0,
     threshold: INITIAL_THRESHOLD,
     boxes_start: None,
 }));
 
-pub struct GcBoxHeader {
+pub(crate) struct GcBoxHeader {
     // XXX This is horribly space inefficient - not sure if we care
     // We are using a word word bool - there is a full 63 bits of unused data :(
     // XXX: Should be able to store marked in the high bit of roots?
     roots: Cell<usize>,
-    next: Option<NonNull<GcBox<Trace>>>,
+    next: Option<NonNull<GcBox<dyn Trace>>>,
     marked: Cell<bool>,
 }
 
-pub struct GcBox<T: Trace + ?Sized + 'static> {
+pub(crate) struct GcBox<T: Trace + ?Sized + 'static> {
     header: GcBoxHeader,
     data: T,
 }
@@ -83,7 +83,7 @@ impl<T: Trace> GcBox<T> {
     /// and appends it to the thread-local `GcBox` chain.
     ///
     /// A `GcBox` allocated this way starts its life rooted.
-    pub fn new(value: T) -> NonNull<Self> {
+    pub(crate) fn new(value: T) -> NonNull<Self> {
         GC_STATE.with(|st| {
             let mut st = st.borrow_mut();
 
@@ -121,7 +121,7 @@ impl<T: Trace> GcBox<T> {
 
 impl<T: Trace + ?Sized> GcBox<T> {
     /// Marks this `GcBox` and marks through its data.
-    pub unsafe fn trace_inner(&self) {
+    pub(crate) unsafe fn trace_inner(&self) {
         let marked = self.header.marked.get();
         if !marked {
             self.header.marked.set(true);
@@ -131,7 +131,7 @@ impl<T: Trace + ?Sized> GcBox<T> {
 
     /// Increases the root count on this `GcBox`.
     /// Roots prevent the `GcBox` from being destroyed by the garbage collector.
-    pub unsafe fn root_inner(&self) {
+    pub(crate) unsafe fn root_inner(&self) {
         // abort if the count overflows to prevent `mem::forget` loops that could otherwise lead to
         // erroneous drops
         self.header.roots.set(self.header.roots.get().checked_add(1).unwrap());
@@ -139,12 +139,12 @@ impl<T: Trace + ?Sized> GcBox<T> {
 
     /// Decreases the root count on this `GcBox`.
     /// Roots prevent the `GcBox` from being destroyed by the garbage collector.
-    pub unsafe fn unroot_inner(&self) {
+    pub(crate) unsafe fn unroot_inner(&self) {
         self.header.roots.set(self.header.roots.get() - 1);
     }
 
     /// Returns a reference to the `GcBox`'s value.
-    pub fn value(&self) -> &T {
+    pub(crate) fn value(&self) -> &T {
         &self.data
     }
 }
@@ -152,10 +152,10 @@ impl<T: Trace + ?Sized> GcBox<T> {
 /// Collects garbage.
 fn collect_garbage(st: &mut GcState) {
     struct Unmarked {
-        incoming: *mut Option<NonNull<GcBox<Trace>>>,
-        this: NonNull<GcBox<Trace>>,
+        incoming: *mut Option<NonNull<GcBox<dyn Trace>>>,
+        this: NonNull<GcBox<dyn Trace>>,
     }
-    unsafe fn mark(head: &mut Option<NonNull<GcBox<Trace>>>) -> Vec<Unmarked> {
+    unsafe fn mark(head: &mut Option<NonNull<GcBox<dyn Trace>>>) -> Vec<Unmarked> {
         // Walk the tree, tracing and marking the nodes
         let mut mark_head = *head;
         while let Some(node) = mark_head {
diff --git a/gc/src/lib.rs b/gc/src/lib.rs
index bd5a0bc..30a5fe1 100644
--- a/gc/src/lib.rs
+++ b/gc/src/lib.rs
@@ -9,7 +9,7 @@
     feature(coerce_unsized, optin_builtin_traits, unsize, specialization)
 )]
 
-use gc::GcBox;
+use crate::gc::GcBox;
 use std::alloc::Layout;
 use std::cell::{Cell, UnsafeCell};
 use std::cmp::Ordering;
@@ -31,8 +31,8 @@ mod trace;
 
 // We re-export the Trace method, as well as some useful internal methods for
 // managing collections or configuring the garbage collector.
-pub use gc::{finalizer_safe, force_collect};
-pub use trace::{Finalize, Trace};
+pub use crate::gc::{finalizer_safe, force_collect};
+pub use crate::trace::{Finalize, Trace};
 
 ////////
 // Gc //
@@ -283,11 +283,6 @@ impl<T: Trace + ?Sized + PartialEq> PartialEq for Gc<T> {
     fn eq(&self, other: &Self) -> bool {
         **self == **other
     }
-
-    #[inline(always)]
-    fn ne(&self, other: &Self) -> bool {
-        **self != **other
-    }
 }
 
 impl<T: Trace + ?Sized + Eq> Eq for Gc<T> {}
@@ -333,19 +328,19 @@ impl<T: Trace + ?Sized + Hash> Hash for Gc<T> {
 }
 
 impl<T: Trace + ?Sized + Display> Display for Gc<T> {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         Display::fmt(&**self, f)
     }
 }
 
 impl<T: Trace + ?Sized + Debug> Debug for Gc<T> {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         Debug::fmt(&**self, f)
     }
 }
 
 impl<T: Trace> fmt::Pointer for Gc<T> {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         fmt::Pointer::fmt(&self.inner(), f)
     }
 }
@@ -473,7 +468,7 @@ impl<T: Trace + ?Sized> GcCell<T> {
     ///
     /// Panics if the value is currently mutably borrowed.
     #[inline]
-    pub fn borrow(&self) -> GcCellRef<T> {
+    pub fn borrow(&self) -> GcCellRef<'_, T> {
         if self.flags.get().borrowed() == BorrowState::Writing {
             panic!("GcCell<T> already mutably borrowed");
         }
@@ -500,7 +495,7 @@ impl<T: Trace + ?Sized> GcCell<T> {
     ///
     /// Panics if the value is currently borrowed.
     #[inline]
-    pub fn borrow_mut(&self) -> GcCellRefMut<T> {
+    pub fn borrow_mut(&self) -> GcCellRefMut<'_, T> {
         if self.flags.get().borrowed() != BorrowState::Unused {
             panic!("GcCell<T> already borrowed");
         }
@@ -587,7 +582,7 @@ impl<'a, T: Trace + ?Sized> Drop for GcCellRef<'a, T> {
 }
 
 impl<'a, T: Trace + ?Sized + Debug> Debug for GcCellRef<'a, T> {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         Debug::fmt(&**self, f)
     }
 }
@@ -630,7 +625,7 @@ impl<'a, T: Trace + ?Sized> Drop for GcCellRefMut<'a, T> {
 }
 
 impl<'a, T: Trace + ?Sized + Debug> Debug for GcCellRefMut<'a, T> {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         Debug::fmt(&*(self.deref()), f)
     }
 }
@@ -656,11 +651,6 @@ impl<T: Trace + ?Sized + PartialEq> PartialEq for GcCell<T> {
     fn eq(&self, other: &Self) -> bool {
         *self.borrow() == *other.borrow()
     }
-
-    #[inline(always)]
-    fn ne(&self, other: &Self) -> bool {
-        *self.borrow() != *other.borrow()
-    }
 }
 
 impl<T: Trace + ?Sized + Eq> Eq for GcCell<T> {}
@@ -700,7 +690,7 @@ impl<T: Trace + ?Sized + Ord> Ord for GcCell<T> {
 }
 
 impl<T: Trace + ?Sized + Debug> Debug for GcCell<T> {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match self.flags.get().borrowed() {
             BorrowState::Unused | BorrowState::Reading => f
                 .debug_struct("GcCell")
diff --git a/gc/src/trace.rs b/gc/src/trace.rs
index 7510899..8fe9444 100644
--- a/gc/src/trace.rs
+++ b/gc/src/trace.rs
@@ -1,5 +1,5 @@
 use std::collections::{BinaryHeap, BTreeMap, BTreeSet, HashMap, HashSet, LinkedList, VecDeque};
-use std::hash::Hash;
+use std::hash::{Hash, BuildHasher};
 use std::path::{Path, PathBuf};
 use std::sync::atomic::{AtomicBool, AtomicIsize, AtomicUsize};
 
@@ -248,7 +248,7 @@ unsafe impl<T: Trace, E: Trace> Trace for Result<T, E> {
 impl<T: Ord + Trace> Finalize for BinaryHeap<T> {}
 unsafe impl<T: Ord + Trace> Trace for BinaryHeap<T> {
     custom_trace!(this, {
-        for v in this.into_iter() {
+        for v in this.iter() {
             mark(v);
         }
     });
@@ -273,8 +273,8 @@ unsafe impl<T: Trace> Trace for BTreeSet<T> {
     });
 }
 
-impl<K: Eq + Hash + Trace, V: Trace> Finalize for HashMap<K, V> {}
-unsafe impl<K: Eq + Hash + Trace, V: Trace> Trace for HashMap<K, V> {
+impl<K: Eq + Hash + Trace, V: Trace, S: BuildHasher> Finalize for HashMap<K, V, S> {}
+unsafe impl<K: Eq + Hash + Trace, V: Trace, S: BuildHasher> Trace for HashMap<K, V, S> {
     custom_trace!(this, {
         for (k, v) in this.iter() {
             mark(k);
@@ -283,8 +283,8 @@ unsafe impl<K: Eq + Hash + Trace, V: Trace> Trace for HashMap<K, V> {
     });
 }
 
-impl<T: Eq + Hash + Trace> Finalize for HashSet<T> {}
-unsafe impl<T: Eq + Hash + Trace> Trace for HashSet<T> {
+impl<T: Eq + Hash + Trace, S: BuildHasher> Finalize for HashSet<T, S> {}
+unsafe impl<T: Eq + Hash + Trace, S: BuildHasher> Trace for HashSet<T, S> {
     custom_trace!(this, {
         for v in this.iter() {
             mark(v);
diff --git a/gc/tests/finalize.rs b/gc/tests/finalize.rs
index 92e99d3..b56a19c 100644
--- a/gc/tests/finalize.rs
+++ b/gc/tests/finalize.rs
@@ -1,10 +1,7 @@
 #![cfg_attr(feature = "nightly", feature(specialization))]
 
-#[macro_use]
-extern crate gc_derive;
-extern crate gc;
-
 use gc::{Finalize, Trace};
+use gc_derive::{Trace, Finalize};
 use std::cell::Cell;
 
 #[derive(PartialEq, Eq, Debug, Clone, Copy)]
diff --git a/gc/tests/gc_semantics.rs b/gc/tests/gc_semantics.rs
index cd5028f..40582a7 100644
--- a/gc/tests/gc_semantics.rs
+++ b/gc/tests/gc_semantics.rs
@@ -1,12 +1,9 @@
 #![cfg_attr(feature = "nightly", feature(specialization))]
 
-#[macro_use]
-extern crate gc_derive;
-extern crate gc;
-
 use std::cell::Cell;
 use std::thread::LocalKey;
 use gc::{Trace, Finalize, GcCell, Gc, force_collect};
+use gc_derive::{Trace, Finalize};
 
 // Utility methods for the tests
 #[derive(PartialEq, Eq, Debug, Clone, Copy)]
@@ -21,11 +18,11 @@ struct GcWatchFlags {
 impl GcWatchFlags {
     fn new(trace: i32, root: i32, unroot: i32, drop: i32, finalize: i32) -> GcWatchFlags {
         GcWatchFlags {
-            trace: trace,
-            root: root,
-            unroot: unroot,
-            drop: drop,
-            finalize: finalize,
+            trace,
+            root,
+            unroot,
+            drop,
+            finalize,
         }
     }
 
diff --git a/gc/tests/gymnastics_cycle.rs b/gc/tests/gymnastics_cycle.rs
index 79ec9a0..34ad8a5 100644
--- a/gc/tests/gymnastics_cycle.rs
+++ b/gc/tests/gymnastics_cycle.rs
@@ -1,11 +1,8 @@
 #![cfg_attr(feature = "nightly", feature(specialization))]
 
-#[macro_use]
-extern crate gc_derive;
-extern crate gc;
-
 use std::cell::Cell;
 use gc::{GcCell, Gc, force_collect};
+use gc_derive::Trace;
 
 thread_local!(static COUNTER: Cell<u8> = Cell::new(0u8));
 
diff --git a/gc/tests/i128.rs b/gc/tests/i128.rs
index 821fceb..e1b9c77 100644
--- a/gc/tests/i128.rs
+++ b/gc/tests/i128.rs
@@ -1,7 +1,3 @@
-#![cfg_attr(feature = "nightly", feature(i128_type))]
-
-extern crate gc;
-
 use gc::Gc;
 
 #[test]
diff --git a/gc/tests/trace_impl.rs b/gc/tests/trace_impl.rs
index 29220c1..6737ba0 100644
--- a/gc/tests/trace_impl.rs
+++ b/gc/tests/trace_impl.rs
@@ -1,8 +1,6 @@
 #![cfg_attr(feature = "nightly", feature(specialization))]
 
-#[macro_use]
-extern crate gc_derive;
-extern crate gc;
+use gc_derive::{Trace, Finalize};
 use std::cell::RefCell;
 
 thread_local!(static X: RefCell<u8> = RefCell::new(0));
@@ -16,7 +14,7 @@ unsafe impl Trace for Foo {
     unsafe fn trace(&self) {
         X.with(|x| {
             let mut m = x.borrow_mut();
-            *m = *m + 1;
+            *m += 1;
         })
     }
     unsafe fn root(&self) {}
diff --git a/gc_derive/Cargo.toml b/gc_derive/Cargo.toml
index cbb1cda..083b803 100644
--- a/gc_derive/Cargo.toml
+++ b/gc_derive/Cargo.toml
@@ -8,6 +8,7 @@ repository = "https://github.com/Manishearth/rust-gc"
 readme = "../README.md"
 license = "MPL-2.0"
 keywords = ["garbage", "macro", "memory"]
+edition = "2018"
 
 [lib]
 name = "gc_derive"
diff --git a/gc_derive/src/lib.rs b/gc_derive/src/lib.rs
index 34e931f..df69991 100644
--- a/gc_derive/src/lib.rs
+++ b/gc_derive/src/lib.rs
@@ -1,14 +1,9 @@
-extern crate proc_macro;
-extern crate syn;
-#[macro_use]
-extern crate synstructure;
-#[macro_use]
-extern crate quote;
-extern crate proc_macro2;
+use synstructure::{decl_derive, Structure};
+use quote::quote;
 
 decl_derive!([Trace, attributes(unsafe_ignore_trace)] => derive_trace);
 
-fn derive_trace(mut s: synstructure::Structure) -> proc_macro2::TokenStream {
+fn derive_trace(mut s: Structure<'_>) -> proc_macro2::TokenStream {
     s.filter(|bi| !bi.ast().attrs.iter().any(|attr| attr.path.is_ident("unsafe_ignore_trace")));
     let trace_body = s.each(|bi| quote!(mark(#bi)));
 
@@ -67,6 +62,6 @@ fn derive_trace(mut s: synstructure::Structure) -> proc_macro2::TokenStream {
 
 decl_derive!([Finalize] => derive_finalize);
 
-fn derive_finalize(s: synstructure::Structure) -> proc_macro2::TokenStream {
+fn derive_finalize(s: Structure<'_>) -> proc_macro2::TokenStream {
     s.unbound_impl(quote!(::gc::Finalize), quote!())
 }