diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml
index ff55075e..91e8645d 100644
--- a/.github/workflows/rust.yml
+++ b/.github/workflows/rust.yml
@@ -70,3 +70,6 @@ jobs:
       - name: Clean and Run tests for derive
         if: ${{ matrix.build == 'nightly' }}
         run: cd proptest-derive && cargo clean && cargo test
+      - name: Clean and Run tests for derive with features enabled
+        if: ${{ matrix.build == 'nightly' }}
+        run: cd proptest-derive && cargo clean && cargo test --features boxed_union
diff --git a/proptest-derive/Cargo.toml b/proptest-derive/Cargo.toml
index 0462985a..b6421a4f 100644
--- a/proptest-derive/Cargo.toml
+++ b/proptest-derive/Cargo.toml
@@ -37,6 +37,10 @@ proc-macro2 = "1.0"
 syn = { version = "1.0.0", features = ["visit", "extra-traits", "full"] }
 quote = "1.0"
 
+[features]
+# Don't generate TupleUnion structs in #[derive(Arbitrary)] code
+boxed_union = []
+
 [[bench]]
 name = "large_enum"
 harness = false
diff --git a/proptest-derive/src/ast.rs b/proptest-derive/src/ast.rs
index f993c9b3..178a09e3 100644
--- a/proptest-derive/src/ast.rs
+++ b/proptest-derive/src/ast.rs
@@ -29,6 +29,7 @@ use crate::util::self_ty;
 /// Increase this if the behaviour is changed in `proptest`.
 /// Keeping this lower than what `proptest` supports will also work
 /// but for optimality this should follow what `proptest` supports.
+#[cfg(not(feature = "boxed_union"))]
 const UNION_CHUNK_SIZE: usize = 9;
 
 /// The `MAX - 1` tuple length `Arbitrary` is implemented for. After this number,
@@ -380,7 +381,10 @@ impl ToTokens for Strategy {
                     >
                 )
             }
+            #[cfg(not(feature = "boxed_union"))]
             Union(strats) => union_strat_to_tokens(tokens, strats),
+            #[cfg(feature = "boxed_union")]
+            Union(strats) => union_strat_to_tokens_boxed(tokens, strats),
             Filter(strat, ty) => quote_append!(tokens,
                 _proptest::strategy::Filter<#strat, fn(&#ty) -> bool>
             ),
@@ -508,14 +512,17 @@ impl ToTokens for Ctor {
             ),
             Existential(expr) => quote_append!(tokens,
                 _proptest::strategy::Strategy::boxed( #expr ) ),
-            Value(expr) => quote_append!(tokens, || #expr ),
+            Value(expr) => quote_append!(tokens, (|| #expr) as fn() -> _),
             ValueExistential(expr) => quote_append!(tokens,
                 _proptest::strategy::Strategy::boxed(
                     _proptest::strategy::LazyJust::new(move || #expr)
                 )
             ),
             Map(ctors, closure) => map_ctor_to_tokens(tokens, &ctors, closure),
+            #[cfg(not(feature = "boxed_union"))]
             Union(ctors) => union_ctor_to_tokens(tokens, ctors),
+            #[cfg(feature = "boxed_union")]
+            Union(ctors) => union_ctor_to_tokens_boxed(tokens, ctors),
         }
     }
 }
@@ -607,6 +614,7 @@ fn map_ctor_to_tokens(
 ///     (10, 11, 12, 13, 14, 15, 16, 17, 18,
 ///         (19, ..)))
 /// ```
+#[cfg(not(feature = "boxed_union"))]
 fn union_ctor_to_tokens(tokens: &mut TokenStream, ctors: &[(u32, Ctor)]) {
     if ctors.is_empty() {
         return;
@@ -665,6 +673,7 @@ fn union_ctor_to_tokens(tokens: &mut TokenStream, ctors: &[(u32, Ctor)]) {
 
 /// Tokenizes a weighted list of `Strategy`.
 /// For details, see `union_ctor_to_tokens`.
+#[cfg(not(feature = "boxed_union"))]
 fn union_strat_to_tokens(tokens: &mut TokenStream, strats: &[Strategy]) {
     if strats.is_empty() {
         return;
@@ -714,6 +723,55 @@ fn union_strat_to_tokens(tokens: &mut TokenStream, strats: &[Strategy]) {
     }
 }
 
+/// Tokenizes a weighted list of `Ctor`.
+///
+/// This can be used instead of `union_ctor_to_tokens` to generate a boxing
+/// macro.
+#[cfg(feature = "boxed_union")]
+fn union_ctor_to_tokens_boxed(tokens: &mut TokenStream, ctors: &[(u32, Ctor)]) {
+    if ctors.is_empty() {
+        return;
+    }
+
+    if let [(_, ctor)] = ctors {
+        // This is not a union at all - user provided an enum with one variant.
+        ctor.to_tokens(tokens);
+        return;
+    }
+
+    let ctors_boxed = ctors.iter().map(wrap_boxed);
+
+    quote_append!(
+        tokens,
+        _proptest::strategy::Union::new_weighted(vec![ #(#ctors_boxed,)* ])
+    );
+
+    fn wrap_boxed(arg: &(u32, Ctor)) -> TokenStream {
+        let (w, c) = arg;
+        quote!( (#w, _proptest::strategy::Strategy::boxed(#c)) )
+    }
+}
+
+/// Tokenizes a weighted list of `Strategy`.
+/// For details, see `union_ctor_to_tokens_boxed`.
+#[cfg(feature = "boxed_union")]
+fn union_strat_to_tokens_boxed(tokens: &mut TokenStream, strats: &[Strategy]) {
+    if strats.is_empty() {
+        return;
+    }
+
+    if let [strat] = strats {
+        // This is not a union at all - user provided an enum with one variant.
+        strat.to_tokens(tokens);
+        return;
+    }
+
+    quote_append!(
+        tokens,
+        _proptest::strategy::Union<_proptest::strategy::BoxedStrategy<Self>>
+    );
+}
+
 /// Wraps a `Ctor` that expects the `to` "register" to be filled with
 /// contents of the `from` register. The correctness of this wrt. the
 /// generated Rust code has to be verified externally by checking the
diff --git a/proptest-derive/src/tests.rs b/proptest-derive/src/tests.rs
index 4ee63fe9..788a6e7c 100644
--- a/proptest-derive/src/tests.rs
+++ b/proptest-derive/src/tests.rs
@@ -87,7 +87,7 @@ test! {
             type Strategy = fn() -> Self;
 
             fn arbitrary_with(_top: Self::Parameters) -> Self::Strategy {
-                || MyUnitStruct {}
+                (|| MyUnitStruct {}) as fn() -> _
             }
         }
         };
@@ -108,7 +108,7 @@ test! {
             type Strategy = fn() -> Self;
 
             fn arbitrary_with(_top: Self::Parameters) -> Self::Strategy {
-                || MyTupleUnitStruct {}
+                (|| MyTupleUnitStruct {}) as fn() -> _
             }
         }
         };
@@ -129,7 +129,7 @@ test! {
             type Strategy = fn() -> Self;
 
             fn arbitrary_with(_top: Self::Parameters) -> Self::Strategy {
-                || MyNamedUnitStruct {}
+                (|| MyNamedUnitStruct {}) as fn () -> _
             }
         }
         };