diff --git a/.travis.yml b/.travis.yml
index c7e4953..1c6e928 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -18,11 +18,6 @@ jobs:
             done
             (exit $CLIPPY_STATUS)
           fi
-    - name: "Stable"
-      rust: stable
-      script:
-        # Skip `--all-features` because stable Rocket isn't released yet
-        - cargo test --workspace
     - name: "Benchmarks"
       script:
         - (cd benchmarks && cargo test --benches --locked)
diff --git a/benchmarks/Cargo.lock b/benchmarks/Cargo.lock
index 7d802bb..4d5cd97 100644
--- a/benchmarks/Cargo.lock
+++ b/benchmarks/Cargo.lock
@@ -413,7 +413,6 @@ version = "0.22.0"
 dependencies = [
  "maud_htmlescape",
  "maud_macros",
- "version_check",
 ]
 
 [[package]]
diff --git a/docs/Cargo.lock b/docs/Cargo.lock
index f954bf7..1218e8b 100644
--- a/docs/Cargo.lock
+++ b/docs/Cargo.lock
@@ -295,7 +295,6 @@ version = "0.22.0"
 dependencies = [
  "maud_htmlescape",
  "maud_macros",
- "version_check",
 ]
 
 [[package]]
diff --git a/maud/Cargo.toml b/maud/Cargo.toml
index 3a6a49e..a80e3d0 100644
--- a/maud/Cargo.toml
+++ b/maud/Cargo.toml
@@ -25,9 +25,6 @@ rocket = { version = ">= 0.3, < 0.5", optional = true }
 futures = { version = "0.3.0", optional = true }
 actix-web-dep = { version = "2.0.0", optional = true, default-features = false, package = "actix-web" }
 
-[build-dependencies]
-version_check = "0.9.2"
-
 [dev-dependencies]
 trybuild = { version = "1.0.33", features = ["diff"] }
 
diff --git a/maud/build.rs b/maud/build.rs
deleted file mode 100644
index cf8eb1d..0000000
--- a/maud/build.rs
+++ /dev/null
@@ -1,5 +0,0 @@
-fn main() {
-    if version_check::is_feature_flaggable() == Some(true) {
-        println!("cargo:rustc-cfg=unstable");
-    }
-}
diff --git a/maud/src/lib.rs b/maud/src/lib.rs
index 9100587..e1bb924 100644
--- a/maud/src/lib.rs
+++ b/maud/src/lib.rs
@@ -1,5 +1,3 @@
-#![cfg_attr(unstable, feature(min_specialization))]
-
 //! A macro for writing HTML templates.
 //!
 //! This documentation only describes the runtime API. For a general
@@ -78,31 +76,45 @@ pub trait Render {
     }
 }
 
-#[cfg(not(unstable))]
 impl<T: fmt::Display + ?Sized> Render for T {
     fn render_to(&self, w: &mut String) {
         let _ = write!(Escaper::new(w), "{}", self);
     }
 }
 
-#[cfg(unstable)]
-impl<T: fmt::Display + ?Sized> Render for T {
-    default fn render_to(&self, w: &mut String) {
-        let _ = write!(Escaper::new(w), "{}", self);
-    }
-}
+/// Spicy hack to specialize `Render` for `T: AsRef<str>`.
+///
+/// The `std::fmt` machinery is rather heavyweight, both in code size and speed.
+/// It would be nice to skip this overhead for the common cases of `&str` and
+/// `String`. But the obvious solution uses *specialization*, which (as of this
+/// writing) requires Nightly. The [*inherent method specialization*][1] trick
+/// is less clear but works on Stable.
+///
+/// This module is an implementation detail and should not be used directly.
+///
+/// [1]: https://github.com/dtolnay/case-studies/issues/14
+#[doc(hidden)]
+pub mod render {
+    use crate::Render;
+    use maud_htmlescape::Escaper;
+    use std::fmt::Write;
 
-#[cfg(unstable)]
-impl Render for String {
-    fn render_to(&self, w: &mut String) {
-        let _ = Escaper::new(w).write_str(self);
+    pub trait RenderInternal {
+        fn __maud_render_to(&self, w: &mut String);
     }
-}
 
-#[cfg(unstable)]
-impl Render for str {
-    fn render_to(&self, w: &mut String) {
-        let _ = Escaper::new(w).write_str(self);
+    pub struct RenderWrapper<'a, T: ?Sized>(pub &'a T);
+
+    impl<'a, T: AsRef<str> + ?Sized> RenderWrapper<'a, T> {
+        pub fn __maud_render_to(&self, w: &mut String) {
+            let _ = Escaper::new(w).write_str(self.0.as_ref());
+        }
+    }
+
+    impl<'a, T: Render + ?Sized> RenderInternal for RenderWrapper<'a, T> {
+        fn __maud_render_to(&self, w: &mut String) {
+            self.0.render_to(w);
+        }
     }
 }
 
diff --git a/maud_macros/src/generate.rs b/maud_macros/src/generate.rs
index 976ad98..d3337fc 100644
--- a/maud_macros/src/generate.rs
+++ b/maud_macros/src/generate.rs
@@ -91,14 +91,8 @@ impl Generator {
     fn splice(&self, expr: TokenStream) -> TokenStream {
         let output_ident = self.output_ident.clone();
         quote!({
-            // Create a local trait alias so that autoref works
-            trait Render: maud::Render {
-                fn __maud_render_to(&self, output_ident: &mut ::std::string::String) {
-                    maud::Render::render_to(self, output_ident);
-                }
-            }
-            impl<T: maud::Render> Render for T {}
-            #expr.__maud_render_to(&mut #output_ident);
+            use maud::render::{RenderInternal, RenderWrapper};
+            RenderWrapper(&#expr).__maud_render_to(&mut #output_ident);
         })
     }