update: arbitrary->1.4.1, derive_arbitrary->1.4.1

Bug: 388276501
Test: mm in android-crates-io
Change-Id: Id7c7974fc1bc2dfeb5873a3e315d34f25af4ef25
diff --git a/crates/arbitrary/.android-checksum.json b/crates/arbitrary/.android-checksum.json
new file mode 100644
index 0000000..68a31d9
--- /dev/null
+++ b/crates/arbitrary/.android-checksum.json
@@ -0,0 +1 @@
+{"package":null,"files":{".cargo-checksum.json":"de362fff988b9f1d462a9ab7cc592643ef52242a24e18a32d736e670c42c8ad0","Android.bp":"4bc1007f1349c0dd73e3bf4bb5bdfabb0c6fab62d5482eb9eef4f03afafc8402","CHANGELOG.md":"acff0f841ed9aa0f4e9e05ad406176a8029d7daf2044b1d0588bef0f6abef4f6","Cargo.lock":"cf062320cb16dc86c0a74589bdbef5db606c401e6a944e9b3b3461ade3c48f98","Cargo.toml":"d13ac9fac79450ced958fada3a81eae2552108d88c44765019cdd81a1ec9be5e","LICENSE":"3c7cd2396b5b772507febd2615d3d5a55b80103845037df77c87ba6e64872f2c","LICENSE-APACHE":"3c7cd2396b5b772507febd2615d3d5a55b80103845037df77c87ba6e64872f2c","LICENSE-MIT":"3b270c5247fc2265dac124e9ada8609aa00c03289761bafa13c741aee60a504a","METADATA":"5ed74da37fc713266c41540eecc4caabfa53a24e0a48b7c07db1233b5494d833","MODULE_LICENSE_APACHE2":"0d6f8afa3940b7f06bebee651376d43bc8b0d5b437337be2696d30377451e93a","NOTICE":"3c7cd2396b5b772507febd2615d3d5a55b80103845037df77c87ba6e64872f2c","README.md":"7c4cd865df07e51893f62d4284fd38060a80b4299506bde81b93a47353a25cd5","TEST_MAPPING":"c0543deb187cac29e27cf8953de57a0697a6ef1710cfad301c6152bbb256b424","cargo_embargo.json":"8c3d9f3c87e4f3b45d16936248c08499f13757a6f5ee7aa48069e8462c2e867b","examples/derive_enum.rs":"f0da81007fc5cf755d4367bc5bd4b01b8bf681e71d252551228f12a2cfba8b09","publish.sh":"950f3f763afd7c09855814e7fb002e438f14530b043fb2a807578228b2829802","src/error.rs":"abdcfad0bc9cecc2ca2c062512ed8e971d26ff560de632d8b5b68b93ca1eca39","src/foreign/alloc/borrow.rs":"a032b34c3e7cb33cc1998023e4075262ef03e6232803ac6860eabac8142c1aaf","src/foreign/alloc/boxed.rs":"f7a8cf03d7fc28e4fc70b5fc2213e250bea4587fa43452f956d8ed20d7d64cf6","src/foreign/alloc/collections/binary_heap.rs":"8d1aba92d4d7475316527217e4d6325494dfffb7255ee36f57e709b1fc2e8fb0","src/foreign/alloc/collections/btree_map.rs":"7d15e4acfbecd9152b659a2c515a5703b478f81d4a43c5de6df4de0b46336609","src/foreign/alloc/collections/btree_set.rs":"89bf8b9d9c74649714bba5bbfa6184491cfeeece2ce31d9aa91320b305d6aff6","src/foreign/alloc/collections/linked_list.rs":"ec297c92f2416624efe93e7fcf27d03eeb67c460dda6b765a033dc5291c2e602","src/foreign/alloc/collections/mod.rs":"8f35b9a12cb837c3344c4501ca31497e6922746d83ee3a8824739bcf521c2c28","src/foreign/alloc/collections/vec_deque.rs":"960d10c116d920cd28980a1b54810aba000cd7bcf3c40ad06183d7f0f62a13c4","src/foreign/alloc/ffi/c_str.rs":"52dd537b1bf2c11062482c27eed5ae78610699c0963b2faf9c5a10804e062cc2","src/foreign/alloc/ffi/mod.rs":"4ea7541a08577031bb0af4dd5ca36187d3462a125e85e3ed1014c51eee1a7a75","src/foreign/alloc/mod.rs":"3537453799caa122bd9138ea85c51b97997c80a22606f9530a4d786dbeb8a015","src/foreign/alloc/rc.rs":"50b8318e35e66b912e718e894f36c6a0c93e253158ffc6b05fc7ed77d6b207d4","src/foreign/alloc/string.rs":"032de94b1b28ba16af388d8ca89af9dbf2a4ec2f950d00aeaa6ea36ebce915ef","src/foreign/alloc/sync.rs":"8f1fbc6f7412ad43e114cc73eb309d320351bdb4083a4c091fabe886169291de","src/foreign/alloc/vec.rs":"40fa7f7bd8a2e2eeef7cd53a0f2f1df34663a30f68712b7a077cc55df8b04fe7","src/foreign/core/array.rs":"0c13d9acd119af393da2dff4ba8478eb1cdabdbef76a36fa28eef5f269243f7d","src/foreign/core/bool.rs":"7de87766f4753f4abe97e85d6a4f0143b0269693ba94fd7ebfb13cdd1cb4e386","src/foreign/core/cell.rs":"6137a6d85854a2731d76baa81da53b7d1eb1244bbcf6d301fe941237ae9ef8d4","src/foreign/core/char.rs":"415752a792acea61337d0240e7a3ddf6baf924450bc8b3e5a1dbd40abe75fcae","src/foreign/core/iter.rs":"fbe5714ead7cd33b84826bc07249fc4a12b646a6b84cd4ae509b49ca0557f46a","src/foreign/core/marker.rs":"bbe0e24a17e0be0f0adc1bd16744ae8fa5652f4bb172e517bf4fab80b27fdf12","src/foreign/core/mod.rs":"1ad61fc51ca3f0432a37718745c75a2072d3f614a12d13b0c59debb002215caf","src/foreign/core/num.rs":"7c378a94eb223bcf121a0dc2d2efd8f9659fac32a92df59b6c35cc9898b6644c","src/foreign/core/ops.rs":"e5f1e8c2e5f0192cb939d1b47da33075a0083ddfd5ecab1650bf48300484ba84","src/foreign/core/option.rs":"07774d7e1eacebbb53b0bf21b4887fd0c757222f3df10546a3f8e2e6d72077c4","src/foreign/core/result.rs":"d945e5cf50aca2da94940de3bc6f2741e1a9c862e2ab0f2485f8f151538504a5","src/foreign/core/slice.rs":"d48412ee49abcec348f4737d908396bf89db3905720598ca93af5979d9377c4c","src/foreign/core/str.rs":"63550f53d73ef952dab534627c5c5e59f7f3cb3d3c05a35e992dae90ce3362df","src/foreign/core/sync/atomic.rs":"e02d231885055fee90f7e41af6b09e503b998ce471b71cd96f6e665ed4bbff4c","src/foreign/core/sync/mod.rs":"4281ca1fc1f3cd553c40f808a6b84b0ba42fa509a609eb9b8dea7720d72aec9f","src/foreign/core/time.rs":"730fe349eead9530ea50b7a1cf0a32e05570c8cce28bf20ae6680d94945a6f09","src/foreign/core/tuple.rs":"bcf0676666495f8cb62b38f708dd47a355cca3f113a50ec72ddb91e77eaf95ae","src/foreign/core/unit.rs":"4945acf688af425358558124925362427c8214b6f49d4b0465bc81c603b53425","src/foreign/mod.rs":"f5e245c3206e1218f524c6c845331a696b1def9e2e6f37fa3470f3232cc5cb67","src/foreign/std/collections/hash_map.rs":"9a9bcfc2399ef048e9a2551bb68babaab42ef73a4c5027a5b2c672838944988f","src/foreign/std/collections/hash_set.rs":"79d5bbc7321aab2efa7a74ea0f2fa854878e2a3cf5bbf6b0c0053bfd1d0a6b9e","src/foreign/std/collections/mod.rs":"4cd786b68ed9c98003064e69149044bc3163f778f377818810864da7d7b813ce","src/foreign/std/ffi/c_str.rs":"c89cc4a5371d0f533f5841b2e9dab3d01650a796874532af43cca673665ca65d","src/foreign/std/ffi/mod.rs":"9e339f96466034a879bd1b5ce8ddebc31920b29da3b2179edbd654cffd56d8f4","src/foreign/std/ffi/os_str.rs":"f5c4430c14d1dffb64284754c3cc7f1f64191055b9a82a1433643ddea09b24b8","src/foreign/std/mod.rs":"ea192c8c6d13eada32ee715d6b8d8841f339ab8efd42140d4326bfb3a079e7de","src/foreign/std/net.rs":"804795d8ab431adcd441df7228b362a4a0b26b3fbbd08059304be248237607d9","src/foreign/std/path.rs":"0e2dfec57c6ac2552ee0f5a777c7ae5492b565591a87fba90a17a60da902a987","src/foreign/std/sync.rs":"cfa86eb3727c1252fcdd827dd49ad87d8d2d0de605adcc12f08ed26a7d9b6f06","src/lib.rs":"e622864f873299ae4379f840d796a7114375291ebbad11dda4724c5cfe6b835d","src/size_hint.rs":"5dcf145a1257dce08d2bae05d2f6bc8c0c31b552410f688154936a89711e5d0d","src/tests.rs":"5e00b3b46070bcfd3e4a93db9dd98959e076ad6426042cc6111b7e1125f814e2","src/unstructured.rs":"5814ccc5c11b505d5f7c8ffcd67bc75c3da81e3d6cd61af2f7d1bfc59f533d65","tests/bound.rs":"97e331d5fad3caca676151bb5a5c30924e29ddf55c98f99445e379278d8fe772","tests/derive.rs":"632a230bb9ed3e4720ca81821c4a628f812d8fa8b1b65fb51fa34fbbe0311214","tests/path.rs":"19926dcdfc9048b0ce044b77eb7385904a4cc4b5399df7480fa1f72c7fec4c2a"}}
\ No newline at end of file
diff --git a/crates/arbitrary/.cargo-checksum.json b/crates/arbitrary/.cargo-checksum.json
index b53838b..ce9040e 100644
--- a/crates/arbitrary/.cargo-checksum.json
+++ b/crates/arbitrary/.cargo-checksum.json
@@ -1 +1 @@
-{"files":{"CHANGELOG.md":"2db35de47ad7a0edb380d919a7506e11028a718a4bff5343f8ddb5727c4abd5f","Cargo.lock":"1b521507672c462c7cf7d9fcbb89e1eebf3e4c84400c22ab217c6b2392911d01","Cargo.toml":"7961c38666b4e00ff7cc94fb2d835da19fa579856b6868118583442a72bb4685","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"15656cc11a8331f28c0986b8ab97220d3e76f98e60ed388b5ffad37dfac4710c","README.md":"4bce3213c030f51154228986fa9b82881a5a979fabe6c65f8e6cd251d324a020","examples/derive_enum.rs":"ef93268fbed2b70c177bebf5757ef1ee0ca5ba0cb0d335d0989a81725463f8c9","publish.sh":"752e221bdd960666b127df15effddd3d789ff3f1762498961fc79ae99f9a27f1","src/error.rs":"33d74ddd002dd8cf73514c93b7e1ecb604718bea69e53f619bb7a87c7f8d71f2","src/lib.rs":"63af26735ee483ada0ebc7698761e6e5278436e643a81628a865b883ed4662f9","src/size_hint.rs":"9762b183f8277ee4955fe5b22552961744b6237286758161a551f904ef43e3eb","src/unstructured.rs":"0a0ab0f889796bee2e1abe59f7b91c87f0a6991c155c2250a093f736ced4e4e6","tests/derive.rs":"5aef1e3a8b709a3e3e0b154ecd51e34bb4d5f8968291ee691326897da58d9658","tests/path.rs":"7e3e5fa26d5671ac19e99ba010a2fa0894fafe996c5d28ee587919e486d7c7c1"},"package":"3e90af4de65aa7b293ef2d09daff88501eb254f58edde2e1ac02c82d873eadad"}
\ No newline at end of file
+{"files":{"CHANGELOG.md":"9efc0ce89509a05f97a8a8fb0e0b63a8e161a171dfb2012568d0578cf08c6653","Cargo.lock":"7f28adbc92015080ac31f75f26e5d3ef0bd6ff479995e63c58435d5c609c3734","Cargo.toml":"846a1554e7ad0841d2d7a8b5f13b419b05eaeaa158e75fdb9a5e6a5b4afafb66","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"15656cc11a8331f28c0986b8ab97220d3e76f98e60ed388b5ffad37dfac4710c","README.md":"5a1c78cfd5295f86d11bf9baf39e7190366b94a2cfb39bb6ad59b26b66addd52","examples/derive_enum.rs":"45d11d8cd0461716b276e2df4a5b31e7ae7ece36f05d42a0d4c055a5e4f02ab1","publish.sh":"752e221bdd960666b127df15effddd3d789ff3f1762498961fc79ae99f9a27f1","src/error.rs":"8cd2f06dc455d28ee00ac8305ca5f53764e8205e280aa3652988497859163e99","src/foreign/alloc/borrow.rs":"2741eea43a139e7c83b92ab237a60f421224c539ef90ae3125be34b1e6943e54","src/foreign/alloc/boxed.rs":"2ea6fe8795b644de7785ff5373ab6728399de531c2bf50b8f501d48e15dc92db","src/foreign/alloc/collections/binary_heap.rs":"69e648323514313a6f9b6e24b1dbe03d3b24830608f04ad8acac043e6eb44a2b","src/foreign/alloc/collections/btree_map.rs":"158e9e611cb7b03b3190ee8bbebe855cf2291bf14c2adc774d4e6edf5ec47650","src/foreign/alloc/collections/btree_set.rs":"16ad32b9edaa871c046a2543614eb18513801fd880f8926343c2cc74ee7251a4","src/foreign/alloc/collections/linked_list.rs":"cad35cd65f824ef4f0931fa6c200d737b2ac56c3940321a7c7c7b7d9b2d86f1f","src/foreign/alloc/collections/mod.rs":"e81c20370de8903f79d53dd89552c00d3ce6ff12280b379a5ab8d8d1adcecaa5","src/foreign/alloc/collections/vec_deque.rs":"0821db085c8303f8f9e4a316a6dbb5529330488ed06c19284a22a6ef72313270","src/foreign/alloc/ffi/c_str.rs":"16228949035611dfa6522fdc8f25f5afc188eef74febbf06769e9edb88a8430f","src/foreign/alloc/ffi/mod.rs":"9e612467614f7a53369b11278d624bca71f1f44156fcb58d891c7d06f6f1425f","src/foreign/alloc/mod.rs":"27e0cdebd51ee3626ac7ae0b6926c25fdacc9c74b337318d35573d33cc9996b9","src/foreign/alloc/rc.rs":"d17102fc5573461b33b995b5e69553f4d1115f40d70a500c6b86cdf1af766e48","src/foreign/alloc/string.rs":"4c67871b59fb0ce1c13c54b2df2e2054cf02986152bcead7a701fc272ded3cbd","src/foreign/alloc/sync.rs":"e95b2d61962247a03524583aa5ecd1ad5abeb2cfe6590b3ccf7f26649f1287ac","src/foreign/alloc/vec.rs":"57ca9806b9381d6b53e46a0b15fa97c1c698ef950686a1502d0b034656a3f23e","src/foreign/core/array.rs":"1d8c7d794f9990ea43166744a7b25937d43f5f438d0a2c0ab086d88ea07258bd","src/foreign/core/bool.rs":"52a7a7e0dbf613c9f96bde05923b8e766cde41493c6bae5a45bf9c1a104d9590","src/foreign/core/cell.rs":"b594f71084136f441d49340c74e5df734df9f6d91a3fd9945d96595c440f0a2b","src/foreign/core/char.rs":"6705cb3025ca782190e15089689d8cd248b02941bb43fcf4cbf3555c5b5a6e8f","src/foreign/core/iter.rs":"8e5ea3c9885db203cdfb55585c7f6d46f70aa626188abf80af58f2c96df38f53","src/foreign/core/marker.rs":"67386fd457adfabcca6c6fdfbc6883782f6ee3bdd8c344e316adc4587de886af","src/foreign/core/mod.rs":"66b354856caa1d1e2a89f999f5ad555d27cbf3b186fe8087b83e86d4a06dea65","src/foreign/core/num.rs":"294b4977e7707d274461d3029fc7b3225fe2dc4d21e0b86f01bed16b24f901b6","src/foreign/core/ops.rs":"f9bfaef1d11aac50dbefbcd2122b912999401a5a1a165caadc7c3e7aa844d61a","src/foreign/core/option.rs":"1bf99de94ac05ac18c36e1eb3c9b01fcd4e5eddb47c82fe2722c8fc327e29cb8","src/foreign/core/result.rs":"13ea4aab756af7de3e1cc1e38aa0a68045c213636809862193a047c36f13f0a7","src/foreign/core/slice.rs":"f42caa49d1a55461c9d515d386fc2b7b2bfc5f0ac8a229d5ca465af5573a453f","src/foreign/core/str.rs":"add0bc52fcc06adcf294f16bf95f3c513ce07a2fd614d8474976cbf743922e2a","src/foreign/core/sync/atomic.rs":"9be6b63f3b932355a5ae0c578e10399b920bde91be9becb640d7e416462b2631","src/foreign/core/sync/mod.rs":"a5ae8ce1fc6b462b1f57993f94bd8cf014d5375728e67dd9c24a21b66cba95c8","src/foreign/core/time.rs":"f7ef56316b1db52a7bcb1ce841862df7d9b91b3f8dad5313eb0f429d988acc9b","src/foreign/core/tuple.rs":"66e9361bec0d62d2f4214ddab266fee2258cbf52c1243f8882612861b2bd98f1","src/foreign/core/unit.rs":"1035c63c6a7757bae8940bf0fbdcc8c49a2ca2ea004d8c39666c5d0b22d921e1","src/foreign/mod.rs":"9ab162a3dc28ad93acf2a8fc2942d0f409de349a4282f42538e09669aec4aaa5","src/foreign/std/collections/hash_map.rs":"aa6c7e6e7cf4afcb207fa43f1edf95dd3ec4d78cc5fe86bf40a8b98b116d5f0f","src/foreign/std/collections/hash_set.rs":"4b36b1e6f096c8690c7c6b106505c53cd58370f9fb9b4683cd753fa7f393bda4","src/foreign/std/collections/mod.rs":"54399ce5574fd1d84b7b0cb4238fa3e898575e89a6724299be009d2172bda02e","src/foreign/std/ffi/c_str.rs":"80c4bcd6e3d79999503b50c9b82c234539cd63905c0745f9f755b91e1fe54f9a","src/foreign/std/ffi/mod.rs":"7b6b2f64278961fb440787161924427fb6f4ebc376243932ecab9409e1dab755","src/foreign/std/ffi/os_str.rs":"e20cce997e2139d51dc345375e3ad8da3385de7d46e3dc288fc4963835fcb0e6","src/foreign/std/mod.rs":"f304d0482650833ce7f3a785add71590019c3e00ce39f68740b98a966100acf3","src/foreign/std/net.rs":"e391211150bd94fc6fd7f8dac0e1e7e0454b6421f69790996efd93fcd5e277a8","src/foreign/std/path.rs":"8ef7a801595763c99a1342eeaa222a08a46e5c5770bc4a167d36cfd933ad9649","src/foreign/std/sync.rs":"bfbde00300424956e7f06df3194547c0cf2a463ec60df47b0ec90d920e063f8b","src/lib.rs":"6b429a3d1689a5d4cc9699bb8a7d53c2ff22c607f1d7eb8b2b1d271f6467a9ab","src/size_hint.rs":"2e409df461eabb90c4b3e6788e99cb9469a134bb28b95bb7faf1ca5061a6be91","src/tests.rs":"9044f1cf9fbf4879c4e91181376b52700d4dacbcdd1c792743c8abd69281d59a","src/unstructured.rs":"68987005eea45fa558398ef22c64ef526fc0480038cec5a006ccf320622bca2b","tests/bound.rs":"32b8ce8083419ecadd66e1fde4da427b437ac9e007ed9d6b5229a4ef24a5d772","tests/derive.rs":"970bee897008c0132075ff8aa14ac9610933746dabc175b626e479637972e03f","tests/path.rs":"7e3e5fa26d5671ac19e99ba010a2fa0894fafe996c5d28ee587919e486d7c7c1"},"package":"dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223"}
\ No newline at end of file
diff --git a/crates/arbitrary/Android.bp b/crates/arbitrary/Android.bp
index adeeb17..183d010 100644
--- a/crates/arbitrary/Android.bp
+++ b/crates/arbitrary/Android.bp
@@ -13,55 +13,14 @@
     license_text: ["LICENSE"],
 }
 
-rust_test {
-    name: "arbitrary_test_src_lib",
-    host_supported: true,
-    crate_name: "arbitrary",
-    cargo_env_compat: true,
-    cargo_pkg_version: "1.2.3",
-    crate_root: "src/lib.rs",
-    test_suites: ["general-tests"],
-    auto_gen_config: true,
-    test_options: {
-        unit_test: true,
-    },
-    edition: "2018",
-    features: [
-        "derive",
-        "derive_arbitrary",
-    ],
-    proc_macros: ["libderive_arbitrary"],
-}
-
-rust_test {
-    name: "arbitrary_test_tests_derive",
-    host_supported: true,
-    crate_name: "derive",
-    cargo_env_compat: true,
-    cargo_pkg_version: "1.2.3",
-    crate_root: "tests/derive.rs",
-    test_suites: ["general-tests"],
-    auto_gen_config: true,
-    test_options: {
-        unit_test: true,
-    },
-    edition: "2018",
-    features: [
-        "derive",
-        "derive_arbitrary",
-    ],
-    rustlibs: ["libarbitrary"],
-    proc_macros: ["libderive_arbitrary"],
-}
-
 rust_library_rlib {
     name: "libarbitrary",
     host_supported: true,
     crate_name: "arbitrary",
     cargo_env_compat: true,
-    cargo_pkg_version: "1.2.3",
+    cargo_pkg_version: "1.4.1",
     crate_root: "src/lib.rs",
-    edition: "2018",
+    edition: "2021",
     features: [
         "derive",
         "derive_arbitrary",
diff --git a/crates/arbitrary/CHANGELOG.md b/crates/arbitrary/CHANGELOG.md
index 2d4bb72..eb270e6 100644
--- a/crates/arbitrary/CHANGELOG.md
+++ b/crates/arbitrary/CHANGELOG.md
@@ -20,7 +20,7 @@
 
 ### Fixed
 
-* (Included in `arbitrary_derive`  1.2.1) Fixed bug in Derive macro around `no_std` path uses [#131](https://github.com/rust-fuzz/arbitrary/pull/131)
+* TODO (or remove section if none)
 
 ### Security
 
@@ -28,6 +28,70 @@
 
 --------------------------------------------------------------------------------
 
+## 1.4.0
+
+Released 2024-10-30.
+
+### Added
+
+* Added an `Arbitrary` implementation for `PhantomPinned`.
+* Added the `Unstructured::choose_iter` helper method.
+* Added `#[arbitrary(skip)]` for `enum` variants in the derive macro.
+* Added the `Arbitrary::try_size_hint` trait method.
+
+### Changed
+
+* Implement `Arbitrary` for `PhantomData<A>` even when `A` does not implement
+  `Arbitrary` and when `A` is `?Sized`.
+* Make `usize`'s underlying encoding independent of machine word size so that
+  corpora are more portable.
+
+### Fixed
+
+* Make `derive(Arbitrary)` work for local definitions of `struct Option`.
+
+--------------------------------------------------------------------------------
+
+## 1.3.2
+
+Released 2023-10-30.
+
+### Added
+
+* Added `Arbitrary` implementations for `Arc<[T]>` and
+  `Rc<[T]>`. [#160](https://github.com/rust-fuzz/arbitrary/pull/160)
+
+--------------------------------------------------------------------------------
+
+## 1.3.1
+
+Released 2023-10-11.
+
+### Fixed
+
+* Fixed an issue with generating collections of collections in
+  `arbitrary_take_rest` where `<Vec<Vec<u8>>>::arbitrary_take_rest` would never
+  generate `vec![vec![]]` for example. See
+  [#159](https://github.com/rust-fuzz/arbitrary/pull/159) for details.
+
+--------------------------------------------------------------------------------
+
+## 1.3.0
+
+Released 2023-03-13.
+
+### Added
+
+* Added the ability to manually specify derived trait bounds for
+  `Arbitrary`. See [#138](https://github.com/rust-fuzz/arbitrary/pull/138) for
+  details.
+
+### Fixed
+
+* Fixed minimal versions correctness for `syn`.
+
+--------------------------------------------------------------------------------
+
 ## 1.2.3
 
 Released 2023-01-20.
@@ -69,7 +133,7 @@
 
 ## 1.1.6
 
-Released 2022-09-08.
+Released 2022-09-20.
 
 ### Fixed
 
@@ -80,7 +144,7 @@
 
 ## 1.1.5
 
-Released 2022-09-20.
+Released 2022-09-08.
 
 ### Added
 
diff --git a/crates/arbitrary/Cargo.lock b/crates/arbitrary/Cargo.lock
index 26d8335..1cf4e06 100644
--- a/crates/arbitrary/Cargo.lock
+++ b/crates/arbitrary/Cargo.lock
@@ -4,16 +4,17 @@
 
 [[package]]
 name = "arbitrary"
-version = "1.2.3"
+version = "1.4.1"
 dependencies = [
  "derive_arbitrary",
+ "exhaustigen",
 ]
 
 [[package]]
 name = "derive_arbitrary"
-version = "1.2.3"
+version = "1.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8beee4701e2e229e8098bbdecdca12449bc3e322f137d269182fa1291e20bd00"
+checksum = "d475dfebcb4854d596b17b09f477616f80f17a550517f2b3615d8c205d5c802b"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -21,28 +22,34 @@
 ]
 
 [[package]]
-name = "proc-macro2"
-version = "1.0.50"
+name = "exhaustigen"
+version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2"
+checksum = "7d88f747710a968a0a32ce4c4ae90ead21dc36a06aceabbb4367452679a95eb6"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.89"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e"
 dependencies = [
  "unicode-ident",
 ]
 
 [[package]]
 name = "quote"
-version = "1.0.23"
+version = "1.0.37"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
+checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
 dependencies = [
  "proc-macro2",
 ]
 
 [[package]]
 name = "syn"
-version = "1.0.107"
+version = "2.0.87"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
+checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -51,6 +58,6 @@
 
 [[package]]
 name = "unicode-ident"
-version = "1.0.6"
+version = "1.0.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
+checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
diff --git a/crates/arbitrary/Cargo.toml b/crates/arbitrary/Cargo.toml
index 9b11611..8f5a881 100644
--- a/crates/arbitrary/Cargo.toml
+++ b/crates/arbitrary/Cargo.toml
@@ -10,10 +10,10 @@
 # See Cargo.toml.orig for the original contents.
 
 [package]
-edition = "2018"
+edition = "2021"
 rust-version = "1.63.0"
 name = "arbitrary"
-version = "1.2.3"
+version = "1.4.1"
 authors = [
     "The Rust-Fuzz Project Developers",
     "Nick Fitzgerald <[email protected]>",
@@ -22,6 +22,11 @@
     "Brian L. Troutwine <[email protected]>",
     "Corey Farwell <[email protected]>",
 ]
+build = false
+autobins = false
+autoexamples = false
+autotests = false
+autobenches = false
 description = "The trait for generating structured data from unstructured data"
 documentation = "https://docs.rs/arbitrary/"
 readme = "README.md"
@@ -33,18 +38,34 @@
 license = "MIT OR Apache-2.0"
 repository = "https://github.com/rust-fuzz/arbitrary/"
 
+[lib]
+name = "arbitrary"
+path = "src/lib.rs"
+
 [[example]]
 name = "derive_enum"
+path = "examples/derive_enum.rs"
 required-features = ["derive"]
 
 [[test]]
+name = "bound"
+path = "tests/bound.rs"
+
+[[test]]
 name = "derive"
-path = "./tests/derive.rs"
+path = "tests/derive.rs"
 required-features = ["derive"]
 
+[[test]]
+name = "path"
+path = "tests/path.rs"
+
 [dependencies.derive_arbitrary]
-version = "1.2.3"
+version = "1.4.0"
 optional = true
 
+[dev-dependencies.exhaustigen]
+version = "0.1.0"
+
 [features]
 derive = ["derive_arbitrary"]
diff --git a/crates/arbitrary/METADATA b/crates/arbitrary/METADATA
index 836167c..654a476 100644
--- a/crates/arbitrary/METADATA
+++ b/crates/arbitrary/METADATA
@@ -1,17 +1,17 @@
 name: "arbitrary"
 description: "The trait for generating structured data from unstructured data"
 third_party {
-  version: "1.2.3"
+  version: "1.4.1"
   license_type: NOTICE
   last_upgrade_date {
-    year: 2023
-    month: 2
-    day: 1
+    year: 2025
+    month: 1
+    day: 7
   }
   homepage: "https://crates.io/crates/arbitrary"
   identifier {
     type: "Archive"
-    value: "https://static.crates.io/crates/arbitrary/arbitrary-1.2.3.crate"
-    version: "1.2.3"
+    value: "https://static.crates.io/crates/arbitrary/arbitrary-1.4.1.crate"
+    version: "1.4.1"
   }
 }
diff --git a/crates/arbitrary/README.md b/crates/arbitrary/README.md
index 3f5619b..c82309b 100644
--- a/crates/arbitrary/README.md
+++ b/crates/arbitrary/README.md
@@ -84,7 +84,7 @@
     #[arbitrary(with = arbitrary_b)]
     pub b: u8,
 
-    // Generate `a` with a custom closure (shortuct to avoid a custom funciton)
+    // Generate `a` with a custom closure (shortuct to avoid a custom function)
     #[arbitrary(with = |u: &mut Unstructured| u.int_in_range(0..=64))]
     pub a: u8,
 }
diff --git a/crates/arbitrary/examples/derive_enum.rs b/crates/arbitrary/examples/derive_enum.rs
index c4fc9c9..3009f06 100644
--- a/crates/arbitrary/examples/derive_enum.rs
+++ b/crates/arbitrary/examples/derive_enum.rs
@@ -10,9 +10,15 @@
 
 #[derive(Arbitrary, Debug)]
 enum MyEnum {
-    UnitVariant,
-    TupleVariant(bool, u32),
-    StructVariant { x: i8, y: (u8, i32) },
+    Unit,
+    Tuple(bool, u32),
+    Struct {
+        x: i8,
+        y: (u8, i32),
+    },
+
+    #[arbitrary(skip)]
+    Skipped(usize),
 }
 
 fn main() {
diff --git a/crates/arbitrary/src/error.rs b/crates/arbitrary/src/error.rs
index 1d3df2a..6ca8f19 100644
--- a/crates/arbitrary/src/error.rs
+++ b/crates/arbitrary/src/error.rs
@@ -1,7 +1,7 @@
 use std::{error, fmt};
 
 /// An enumeration of buffer creation errors
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 #[non_exhaustive]
 pub enum Error {
     /// No choices were provided to the Unstructured::choose call
diff --git a/crates/arbitrary/src/foreign/alloc/borrow.rs b/crates/arbitrary/src/foreign/alloc/borrow.rs
new file mode 100644
index 0000000..b5b1e22
--- /dev/null
+++ b/crates/arbitrary/src/foreign/alloc/borrow.rs
@@ -0,0 +1,26 @@
+use {
+    crate::{size_hint, Arbitrary, Result, Unstructured},
+    std::borrow::{Cow, ToOwned},
+};
+
+impl<'a, A> Arbitrary<'a> for Cow<'a, A>
+where
+    A: ToOwned + ?Sized + 'a,
+    <A as ToOwned>::Owned: Arbitrary<'a>,
+{
+    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+        Arbitrary::arbitrary(u).map(Cow::Owned)
+    }
+
+    #[inline]
+    fn size_hint(depth: usize) -> (usize, Option<usize>) {
+        Self::try_size_hint(depth).unwrap_or_default()
+    }
+
+    #[inline]
+    fn try_size_hint(depth: usize) -> Result<(usize, Option<usize>), crate::MaxRecursionReached> {
+        size_hint::try_recursion_guard(depth, |depth| {
+            <<A as ToOwned>::Owned as Arbitrary>::try_size_hint(depth)
+        })
+    }
+}
diff --git a/crates/arbitrary/src/foreign/alloc/boxed.rs b/crates/arbitrary/src/foreign/alloc/boxed.rs
new file mode 100644
index 0000000..c3014a3
--- /dev/null
+++ b/crates/arbitrary/src/foreign/alloc/boxed.rs
@@ -0,0 +1,52 @@
+use {
+    crate::{size_hint, Arbitrary, Result, Unstructured},
+    std::boxed::Box,
+};
+
+impl<'a, A> Arbitrary<'a> for Box<A>
+where
+    A: Arbitrary<'a>,
+{
+    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+        Arbitrary::arbitrary(u).map(Self::new)
+    }
+
+    #[inline]
+    fn size_hint(depth: usize) -> (usize, Option<usize>) {
+        Self::try_size_hint(depth).unwrap_or_default()
+    }
+
+    #[inline]
+    fn try_size_hint(depth: usize) -> Result<(usize, Option<usize>), crate::MaxRecursionReached> {
+        size_hint::try_recursion_guard(depth, <A as Arbitrary>::try_size_hint)
+    }
+}
+
+impl<'a, A> Arbitrary<'a> for Box<[A]>
+where
+    A: Arbitrary<'a>,
+{
+    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+        u.arbitrary_iter()?.collect()
+    }
+
+    fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self> {
+        u.arbitrary_take_rest_iter()?.collect()
+    }
+
+    #[inline]
+    fn size_hint(_depth: usize) -> (usize, Option<usize>) {
+        (0, None)
+    }
+}
+
+impl<'a> Arbitrary<'a> for Box<str> {
+    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+        <String as Arbitrary>::arbitrary(u).map(|x| x.into_boxed_str())
+    }
+
+    #[inline]
+    fn size_hint(depth: usize) -> (usize, Option<usize>) {
+        <String as Arbitrary>::size_hint(depth)
+    }
+}
diff --git a/crates/arbitrary/src/foreign/alloc/collections/binary_heap.rs b/crates/arbitrary/src/foreign/alloc/collections/binary_heap.rs
new file mode 100644
index 0000000..25a0384
--- /dev/null
+++ b/crates/arbitrary/src/foreign/alloc/collections/binary_heap.rs
@@ -0,0 +1,22 @@
+use {
+    crate::{Arbitrary, Result, Unstructured},
+    std::collections::binary_heap::BinaryHeap,
+};
+
+impl<'a, A> Arbitrary<'a> for BinaryHeap<A>
+where
+    A: Arbitrary<'a> + Ord,
+{
+    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+        u.arbitrary_iter()?.collect()
+    }
+
+    fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self> {
+        u.arbitrary_take_rest_iter()?.collect()
+    }
+
+    #[inline]
+    fn size_hint(_depth: usize) -> (usize, Option<usize>) {
+        (0, None)
+    }
+}
diff --git a/crates/arbitrary/src/foreign/alloc/collections/btree_map.rs b/crates/arbitrary/src/foreign/alloc/collections/btree_map.rs
new file mode 100644
index 0000000..21b93a4
--- /dev/null
+++ b/crates/arbitrary/src/foreign/alloc/collections/btree_map.rs
@@ -0,0 +1,23 @@
+use {
+    crate::{Arbitrary, Result, Unstructured},
+    std::collections::btree_map::BTreeMap,
+};
+
+impl<'a, K, V> Arbitrary<'a> for BTreeMap<K, V>
+where
+    K: Arbitrary<'a> + Ord,
+    V: Arbitrary<'a>,
+{
+    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+        u.arbitrary_iter()?.collect()
+    }
+
+    fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self> {
+        u.arbitrary_take_rest_iter()?.collect()
+    }
+
+    #[inline]
+    fn size_hint(_depth: usize) -> (usize, Option<usize>) {
+        (0, None)
+    }
+}
diff --git a/crates/arbitrary/src/foreign/alloc/collections/btree_set.rs b/crates/arbitrary/src/foreign/alloc/collections/btree_set.rs
new file mode 100644
index 0000000..8c6e92f
--- /dev/null
+++ b/crates/arbitrary/src/foreign/alloc/collections/btree_set.rs
@@ -0,0 +1,22 @@
+use {
+    crate::{Arbitrary, Result, Unstructured},
+    std::collections::btree_set::BTreeSet,
+};
+
+impl<'a, A> Arbitrary<'a> for BTreeSet<A>
+where
+    A: Arbitrary<'a> + Ord,
+{
+    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+        u.arbitrary_iter()?.collect()
+    }
+
+    fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self> {
+        u.arbitrary_take_rest_iter()?.collect()
+    }
+
+    #[inline]
+    fn size_hint(_depth: usize) -> (usize, Option<usize>) {
+        (0, None)
+    }
+}
diff --git a/crates/arbitrary/src/foreign/alloc/collections/linked_list.rs b/crates/arbitrary/src/foreign/alloc/collections/linked_list.rs
new file mode 100644
index 0000000..6bf2e98
--- /dev/null
+++ b/crates/arbitrary/src/foreign/alloc/collections/linked_list.rs
@@ -0,0 +1,22 @@
+use {
+    crate::{Arbitrary, Result, Unstructured},
+    std::collections::linked_list::LinkedList,
+};
+
+impl<'a, A> Arbitrary<'a> for LinkedList<A>
+where
+    A: Arbitrary<'a>,
+{
+    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+        u.arbitrary_iter()?.collect()
+    }
+
+    fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self> {
+        u.arbitrary_take_rest_iter()?.collect()
+    }
+
+    #[inline]
+    fn size_hint(_depth: usize) -> (usize, Option<usize>) {
+        (0, None)
+    }
+}
diff --git a/crates/arbitrary/src/foreign/alloc/collections/mod.rs b/crates/arbitrary/src/foreign/alloc/collections/mod.rs
new file mode 100644
index 0000000..67ca908
--- /dev/null
+++ b/crates/arbitrary/src/foreign/alloc/collections/mod.rs
@@ -0,0 +1,5 @@
+mod binary_heap;
+mod btree_map;
+mod btree_set;
+mod linked_list;
+mod vec_deque;
diff --git a/crates/arbitrary/src/foreign/alloc/collections/vec_deque.rs b/crates/arbitrary/src/foreign/alloc/collections/vec_deque.rs
new file mode 100644
index 0000000..40e0413
--- /dev/null
+++ b/crates/arbitrary/src/foreign/alloc/collections/vec_deque.rs
@@ -0,0 +1,22 @@
+use {
+    crate::{Arbitrary, Result, Unstructured},
+    std::collections::vec_deque::VecDeque,
+};
+
+impl<'a, A> Arbitrary<'a> for VecDeque<A>
+where
+    A: Arbitrary<'a>,
+{
+    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+        u.arbitrary_iter()?.collect()
+    }
+
+    fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self> {
+        u.arbitrary_take_rest_iter()?.collect()
+    }
+
+    #[inline]
+    fn size_hint(_depth: usize) -> (usize, Option<usize>) {
+        (0, None)
+    }
+}
diff --git a/crates/arbitrary/src/foreign/alloc/ffi/c_str.rs b/crates/arbitrary/src/foreign/alloc/ffi/c_str.rs
new file mode 100644
index 0000000..a1b2383
--- /dev/null
+++ b/crates/arbitrary/src/foreign/alloc/ffi/c_str.rs
@@ -0,0 +1,19 @@
+use {
+    crate::{Arbitrary, Result, Unstructured},
+    std::ffi::CString,
+};
+
+impl<'a> Arbitrary<'a> for CString {
+    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+        <Vec<u8> as Arbitrary>::arbitrary(u).map(|mut x| {
+            x.retain(|&c| c != 0);
+            // SAFETY: all zero bytes have been removed
+            unsafe { Self::from_vec_unchecked(x) }
+        })
+    }
+
+    #[inline]
+    fn size_hint(depth: usize) -> (usize, Option<usize>) {
+        <Vec<u8> as Arbitrary>::size_hint(depth)
+    }
+}
diff --git a/crates/arbitrary/src/foreign/alloc/ffi/mod.rs b/crates/arbitrary/src/foreign/alloc/ffi/mod.rs
new file mode 100644
index 0000000..9238cfc
--- /dev/null
+++ b/crates/arbitrary/src/foreign/alloc/ffi/mod.rs
@@ -0,0 +1 @@
+mod c_str;
diff --git a/crates/arbitrary/src/foreign/alloc/mod.rs b/crates/arbitrary/src/foreign/alloc/mod.rs
new file mode 100644
index 0000000..a04b62c
--- /dev/null
+++ b/crates/arbitrary/src/foreign/alloc/mod.rs
@@ -0,0 +1,13 @@
+//! Implementations of [`Arbitrary`] for [`alloc`] types,
+//!   excluding those in [`core`].
+//!
+//! [`Arbitrary`]: crate::Arbitrary
+
+mod borrow;
+mod boxed;
+mod collections;
+mod ffi;
+mod rc;
+mod string;
+mod sync;
+mod vec;
diff --git a/crates/arbitrary/src/foreign/alloc/rc.rs b/crates/arbitrary/src/foreign/alloc/rc.rs
new file mode 100644
index 0000000..6d58167
--- /dev/null
+++ b/crates/arbitrary/src/foreign/alloc/rc.rs
@@ -0,0 +1,52 @@
+use {
+    crate::{size_hint, Arbitrary, Result, Unstructured},
+    std::rc::Rc,
+};
+
+impl<'a, A> Arbitrary<'a> for Rc<A>
+where
+    A: Arbitrary<'a>,
+{
+    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+        Arbitrary::arbitrary(u).map(Self::new)
+    }
+
+    #[inline]
+    fn size_hint(depth: usize) -> (usize, Option<usize>) {
+        Self::try_size_hint(depth).unwrap_or_default()
+    }
+
+    #[inline]
+    fn try_size_hint(depth: usize) -> Result<(usize, Option<usize>), crate::MaxRecursionReached> {
+        size_hint::try_recursion_guard(depth, <A as Arbitrary>::try_size_hint)
+    }
+}
+
+impl<'a, A> Arbitrary<'a> for Rc<[A]>
+where
+    A: Arbitrary<'a>,
+{
+    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+        u.arbitrary_iter()?.collect()
+    }
+
+    fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self> {
+        u.arbitrary_take_rest_iter()?.collect()
+    }
+
+    #[inline]
+    fn size_hint(_depth: usize) -> (usize, Option<usize>) {
+        (0, None)
+    }
+}
+
+impl<'a> Arbitrary<'a> for Rc<str> {
+    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+        <&str as Arbitrary>::arbitrary(u).map(Into::into)
+    }
+
+    #[inline]
+    fn size_hint(depth: usize) -> (usize, Option<usize>) {
+        <&str as Arbitrary>::size_hint(depth)
+    }
+}
diff --git a/crates/arbitrary/src/foreign/alloc/string.rs b/crates/arbitrary/src/foreign/alloc/string.rs
new file mode 100644
index 0000000..a579784
--- /dev/null
+++ b/crates/arbitrary/src/foreign/alloc/string.rs
@@ -0,0 +1,19 @@
+use {
+    crate::{Arbitrary, Result, Unstructured},
+    std::string::String,
+};
+
+impl<'a> Arbitrary<'a> for String {
+    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+        <&str as Arbitrary>::arbitrary(u).map(Into::into)
+    }
+
+    fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self> {
+        <&str as Arbitrary>::arbitrary_take_rest(u).map(Into::into)
+    }
+
+    #[inline]
+    fn size_hint(depth: usize) -> (usize, Option<usize>) {
+        <&str as Arbitrary>::size_hint(depth)
+    }
+}
diff --git a/crates/arbitrary/src/foreign/alloc/sync.rs b/crates/arbitrary/src/foreign/alloc/sync.rs
new file mode 100644
index 0000000..c8ca1db
--- /dev/null
+++ b/crates/arbitrary/src/foreign/alloc/sync.rs
@@ -0,0 +1,52 @@
+use {
+    crate::{size_hint, Arbitrary, Result, Unstructured},
+    std::sync::Arc,
+};
+
+impl<'a, A> Arbitrary<'a> for Arc<A>
+where
+    A: Arbitrary<'a>,
+{
+    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+        Arbitrary::arbitrary(u).map(Self::new)
+    }
+
+    #[inline]
+    fn size_hint(depth: usize) -> (usize, Option<usize>) {
+        Self::try_size_hint(depth).unwrap_or_default()
+    }
+
+    #[inline]
+    fn try_size_hint(depth: usize) -> Result<(usize, Option<usize>), crate::MaxRecursionReached> {
+        size_hint::try_recursion_guard(depth, <A as Arbitrary>::try_size_hint)
+    }
+}
+
+impl<'a, A> Arbitrary<'a> for Arc<[A]>
+where
+    A: Arbitrary<'a>,
+{
+    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+        u.arbitrary_iter()?.collect()
+    }
+
+    fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self> {
+        u.arbitrary_take_rest_iter()?.collect()
+    }
+
+    #[inline]
+    fn size_hint(_depth: usize) -> (usize, Option<usize>) {
+        (0, None)
+    }
+}
+
+impl<'a> Arbitrary<'a> for Arc<str> {
+    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+        <&str as Arbitrary>::arbitrary(u).map(Into::into)
+    }
+
+    #[inline]
+    fn size_hint(depth: usize) -> (usize, Option<usize>) {
+        <&str as Arbitrary>::size_hint(depth)
+    }
+}
diff --git a/crates/arbitrary/src/foreign/alloc/vec.rs b/crates/arbitrary/src/foreign/alloc/vec.rs
new file mode 100644
index 0000000..63313ba
--- /dev/null
+++ b/crates/arbitrary/src/foreign/alloc/vec.rs
@@ -0,0 +1,22 @@
+use {
+    crate::{Arbitrary, Result, Unstructured},
+    std::vec::Vec,
+};
+
+impl<'a, A> Arbitrary<'a> for Vec<A>
+where
+    A: Arbitrary<'a>,
+{
+    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+        u.arbitrary_iter()?.collect()
+    }
+
+    fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self> {
+        u.arbitrary_take_rest_iter()?.collect()
+    }
+
+    #[inline]
+    fn size_hint(_depth: usize) -> (usize, Option<usize>) {
+        (0, None)
+    }
+}
diff --git a/crates/arbitrary/src/foreign/core/array.rs b/crates/arbitrary/src/foreign/core/array.rs
new file mode 100644
index 0000000..39075be
--- /dev/null
+++ b/crates/arbitrary/src/foreign/core/array.rs
@@ -0,0 +1,76 @@
+use {
+    crate::{size_hint, Arbitrary, Result, Unstructured},
+    core::{
+        array,
+        mem::{self, MaybeUninit},
+        ptr,
+    },
+};
+
+/// Helper to safely create arrays since the standard library doesn't
+/// provide one yet. Shouldn't be necessary in the future.
+struct ArrayGuard<T, const N: usize> {
+    dst: *mut T,
+    initialized: usize,
+}
+
+impl<T, const N: usize> Drop for ArrayGuard<T, N> {
+    fn drop(&mut self) {
+        debug_assert!(self.initialized <= N);
+        let initialized_part = ptr::slice_from_raw_parts_mut(self.dst, self.initialized);
+        unsafe {
+            ptr::drop_in_place(initialized_part);
+        }
+    }
+}
+
+fn try_create_array<F, T, const N: usize>(mut cb: F) -> Result<[T; N]>
+where
+    F: FnMut(usize) -> Result<T>,
+{
+    let mut array: MaybeUninit<[T; N]> = MaybeUninit::uninit();
+    let array_ptr = array.as_mut_ptr();
+    let dst = array_ptr as _;
+    let mut guard: ArrayGuard<T, N> = ArrayGuard {
+        dst,
+        initialized: 0,
+    };
+    unsafe {
+        for (idx, value_ptr) in (*array.as_mut_ptr()).iter_mut().enumerate() {
+            ptr::write(value_ptr, cb(idx)?);
+            guard.initialized += 1;
+        }
+        mem::forget(guard);
+        Ok(array.assume_init())
+    }
+}
+
+impl<'a, T, const N: usize> Arbitrary<'a> for [T; N]
+where
+    T: Arbitrary<'a>,
+{
+    #[inline]
+    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+        try_create_array(|_| <T as Arbitrary<'a>>::arbitrary(u))
+    }
+
+    #[inline]
+    fn arbitrary_take_rest(mut u: Unstructured<'a>) -> Result<Self> {
+        let mut array = Self::arbitrary(&mut u)?;
+        if let Some(last) = array.last_mut() {
+            *last = Arbitrary::arbitrary_take_rest(u)?;
+        }
+        Ok(array)
+    }
+
+    #[inline]
+    fn size_hint(depth: usize) -> (usize, Option<usize>) {
+        Self::try_size_hint(depth).unwrap_or_default()
+    }
+
+    #[inline]
+    fn try_size_hint(depth: usize) -> Result<(usize, Option<usize>), crate::MaxRecursionReached> {
+        let hint = <T as Arbitrary>::try_size_hint(depth)?;
+        Ok(size_hint::and_all(&array::from_fn::<_, N, _>(|_| hint)))
+    }
+}
diff --git a/crates/arbitrary/src/foreign/core/bool.rs b/crates/arbitrary/src/foreign/core/bool.rs
new file mode 100644
index 0000000..42e82fb
--- /dev/null
+++ b/crates/arbitrary/src/foreign/core/bool.rs
@@ -0,0 +1,12 @@
+use crate::{Arbitrary, Result, Unstructured};
+
+impl<'a> Arbitrary<'a> for bool {
+    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+        Ok(<u8 as Arbitrary<'a>>::arbitrary(u)? & 1 == 1)
+    }
+
+    #[inline]
+    fn size_hint(depth: usize) -> (usize, Option<usize>) {
+        <u8 as Arbitrary<'a>>::size_hint(depth)
+    }
+}
diff --git a/crates/arbitrary/src/foreign/core/cell.rs b/crates/arbitrary/src/foreign/core/cell.rs
new file mode 100644
index 0000000..ad0db38
--- /dev/null
+++ b/crates/arbitrary/src/foreign/core/cell.rs
@@ -0,0 +1,61 @@
+use {
+    crate::{Arbitrary, MaxRecursionReached, Result, Unstructured},
+    core::cell::{Cell, RefCell, UnsafeCell},
+};
+
+impl<'a, A> Arbitrary<'a> for Cell<A>
+where
+    A: Arbitrary<'a>,
+{
+    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+        Arbitrary::arbitrary(u).map(Self::new)
+    }
+
+    #[inline]
+    fn size_hint(depth: usize) -> (usize, Option<usize>) {
+        Self::try_size_hint(depth).unwrap_or_default()
+    }
+
+    #[inline]
+    fn try_size_hint(depth: usize) -> Result<(usize, Option<usize>), MaxRecursionReached> {
+        <A as Arbitrary<'a>>::try_size_hint(depth)
+    }
+}
+
+impl<'a, A> Arbitrary<'a> for RefCell<A>
+where
+    A: Arbitrary<'a>,
+{
+    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+        Arbitrary::arbitrary(u).map(Self::new)
+    }
+
+    #[inline]
+    fn size_hint(depth: usize) -> (usize, Option<usize>) {
+        Self::try_size_hint(depth).unwrap_or_default()
+    }
+
+    #[inline]
+    fn try_size_hint(depth: usize) -> Result<(usize, Option<usize>), MaxRecursionReached> {
+        <A as Arbitrary<'a>>::try_size_hint(depth)
+    }
+}
+
+impl<'a, A> Arbitrary<'a> for UnsafeCell<A>
+where
+    A: Arbitrary<'a>,
+{
+    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+        Arbitrary::arbitrary(u).map(Self::new)
+    }
+
+    #[inline]
+    fn size_hint(depth: usize) -> (usize, Option<usize>) {
+        Self::try_size_hint(depth).unwrap_or_default()
+    }
+
+    #[inline]
+    fn try_size_hint(depth: usize) -> Result<(usize, Option<usize>), MaxRecursionReached> {
+        <A as Arbitrary<'a>>::try_size_hint(depth)
+    }
+}
diff --git a/crates/arbitrary/src/foreign/core/char.rs b/crates/arbitrary/src/foreign/core/char.rs
new file mode 100644
index 0000000..1adc62d
--- /dev/null
+++ b/crates/arbitrary/src/foreign/core/char.rs
@@ -0,0 +1,24 @@
+use crate::{Arbitrary, Result, Unstructured};
+
+impl<'a> Arbitrary<'a> for char {
+    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+        // The highest unicode code point is 0x11_FFFF
+        const CHAR_END: u32 = 0x11_0000;
+        // The size of the surrogate blocks
+        const SURROGATES_START: u32 = 0xD800;
+        let mut c = <u32 as Arbitrary<'a>>::arbitrary(u)? % CHAR_END;
+        if let Some(c) = char::from_u32(c) {
+            Ok(c)
+        } else {
+            // We found a surrogate, wrap and try again
+            c -= SURROGATES_START;
+            Ok(char::from_u32(c)
+                .expect("Generated character should be valid! This is a bug in arbitrary-rs"))
+        }
+    }
+
+    #[inline]
+    fn size_hint(depth: usize) -> (usize, Option<usize>) {
+        <u32 as Arbitrary<'a>>::size_hint(depth)
+    }
+}
diff --git a/crates/arbitrary/src/foreign/core/iter.rs b/crates/arbitrary/src/foreign/core/iter.rs
new file mode 100644
index 0000000..8a56f2a
--- /dev/null
+++ b/crates/arbitrary/src/foreign/core/iter.rs
@@ -0,0 +1,18 @@
+use {
+    crate::{Arbitrary, Result, Unstructured},
+    core::iter::{empty, Empty},
+};
+
+impl<'a, A> Arbitrary<'a> for Empty<A>
+where
+    A: Arbitrary<'a>,
+{
+    fn arbitrary(_: &mut Unstructured<'a>) -> Result<Self> {
+        Ok(empty())
+    }
+
+    #[inline]
+    fn size_hint(_depth: usize) -> (usize, Option<usize>) {
+        (0, Some(0))
+    }
+}
diff --git a/crates/arbitrary/src/foreign/core/marker.rs b/crates/arbitrary/src/foreign/core/marker.rs
new file mode 100644
index 0000000..aa0afbe
--- /dev/null
+++ b/crates/arbitrary/src/foreign/core/marker.rs
@@ -0,0 +1,29 @@
+use {
+    crate::{Arbitrary, Result, Unstructured},
+    core::marker::{PhantomData, PhantomPinned},
+};
+
+impl<'a, A> Arbitrary<'a> for PhantomData<A>
+where
+    A: ?Sized,
+{
+    fn arbitrary(_: &mut Unstructured<'a>) -> Result<Self> {
+        Ok(PhantomData)
+    }
+
+    #[inline]
+    fn size_hint(_depth: usize) -> (usize, Option<usize>) {
+        (0, Some(0))
+    }
+}
+
+impl<'a> Arbitrary<'a> for PhantomPinned {
+    fn arbitrary(_: &mut Unstructured<'a>) -> Result<Self> {
+        Ok(PhantomPinned)
+    }
+
+    #[inline]
+    fn size_hint(_depth: usize) -> (usize, Option<usize>) {
+        (0, Some(0))
+    }
+}
diff --git a/crates/arbitrary/src/foreign/core/mod.rs b/crates/arbitrary/src/foreign/core/mod.rs
new file mode 100644
index 0000000..1e525f3
--- /dev/null
+++ b/crates/arbitrary/src/foreign/core/mod.rs
@@ -0,0 +1,20 @@
+//! Implementations of [`Arbitrary`] for [`core`] types.
+//!
+//! [`Arbitrary`]: crate::Arbitrary
+
+mod array;
+mod bool;
+mod cell;
+mod char;
+mod iter;
+mod marker;
+mod num;
+mod ops;
+mod option;
+mod result;
+mod slice;
+mod str;
+mod sync;
+mod time;
+mod tuple;
+mod unit;
diff --git a/crates/arbitrary/src/foreign/core/num.rs b/crates/arbitrary/src/foreign/core/num.rs
new file mode 100644
index 0000000..f0d5b33
--- /dev/null
+++ b/crates/arbitrary/src/foreign/core/num.rs
@@ -0,0 +1,141 @@
+use {
+    crate::{Arbitrary, Error, MaxRecursionReached, Result, Unstructured},
+    core::{
+        mem,
+        num::{
+            NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize, NonZeroU128,
+            NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize, Wrapping,
+        },
+    },
+};
+
+macro_rules! impl_arbitrary_for_integers {
+    ( $( $ty:ty; )* ) => {
+        $(
+            impl<'a> Arbitrary<'a> for $ty {
+                fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+                    let mut buf = [0; mem::size_of::<$ty>()];
+                    u.fill_buffer(&mut buf)?;
+                    Ok(Self::from_le_bytes(buf))
+                }
+
+                #[inline]
+                fn size_hint(_depth: usize) -> (usize, Option<usize>) {
+                    let n = mem::size_of::<$ty>();
+                    (n, Some(n))
+                }
+
+            }
+        )*
+    }
+}
+
+impl_arbitrary_for_integers! {
+    u8;
+    u16;
+    u32;
+    u64;
+    u128;
+    i8;
+    i16;
+    i32;
+    i64;
+    i128;
+}
+
+// Note: We forward Arbitrary for i/usize to i/u64 in order to simplify corpus
+// compatibility between 32-bit and 64-bit builds. This introduces dead space in
+// 32-bit builds but keeps the input layout independent of the build platform.
+impl<'a> Arbitrary<'a> for usize {
+    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+        u.arbitrary::<u64>().map(|x| x as usize)
+    }
+
+    #[inline]
+    fn size_hint(depth: usize) -> (usize, Option<usize>) {
+        <u64 as Arbitrary>::size_hint(depth)
+    }
+}
+
+impl<'a> Arbitrary<'a> for isize {
+    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+        u.arbitrary::<i64>().map(|x| x as isize)
+    }
+
+    #[inline]
+    fn size_hint(depth: usize) -> (usize, Option<usize>) {
+        <i64 as Arbitrary>::size_hint(depth)
+    }
+}
+
+macro_rules! impl_arbitrary_for_floats {
+    ( $( $ty:ident : $unsigned:ty; )* ) => {
+        $(
+            impl<'a> Arbitrary<'a> for $ty {
+                fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+                    Ok(Self::from_bits(<$unsigned as Arbitrary<'a>>::arbitrary(u)?))
+                }
+
+                #[inline]
+                fn size_hint(depth: usize) -> (usize, Option<usize>) {
+                    <$unsigned as Arbitrary<'a>>::size_hint(depth)
+                }
+            }
+        )*
+    }
+}
+
+impl_arbitrary_for_floats! {
+    f32: u32;
+    f64: u64;
+}
+
+macro_rules! implement_nonzero_int {
+    ($nonzero:ty, $int:ty) => {
+        impl<'a> Arbitrary<'a> for $nonzero {
+            fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+                match Self::new(<$int as Arbitrary<'a>>::arbitrary(u)?) {
+                    Some(n) => Ok(n),
+                    None => Err(Error::IncorrectFormat),
+                }
+            }
+
+            #[inline]
+            fn size_hint(depth: usize) -> (usize, Option<usize>) {
+                <$int as Arbitrary<'a>>::size_hint(depth)
+            }
+        }
+    };
+}
+
+implement_nonzero_int! { NonZeroI8, i8 }
+implement_nonzero_int! { NonZeroI16, i16 }
+implement_nonzero_int! { NonZeroI32, i32 }
+implement_nonzero_int! { NonZeroI64, i64 }
+implement_nonzero_int! { NonZeroI128, i128 }
+implement_nonzero_int! { NonZeroIsize, isize }
+implement_nonzero_int! { NonZeroU8, u8 }
+implement_nonzero_int! { NonZeroU16, u16 }
+implement_nonzero_int! { NonZeroU32, u32 }
+implement_nonzero_int! { NonZeroU64, u64 }
+implement_nonzero_int! { NonZeroU128, u128 }
+implement_nonzero_int! { NonZeroUsize, usize }
+
+impl<'a, A> Arbitrary<'a> for Wrapping<A>
+where
+    A: Arbitrary<'a>,
+{
+    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+        Arbitrary::arbitrary(u).map(Wrapping)
+    }
+
+    #[inline]
+    fn size_hint(depth: usize) -> (usize, Option<usize>) {
+        Self::try_size_hint(depth).unwrap_or_default()
+    }
+
+    #[inline]
+    fn try_size_hint(depth: usize) -> Result<(usize, Option<usize>), MaxRecursionReached> {
+        <A as Arbitrary<'a>>::try_size_hint(depth)
+    }
+}
diff --git a/crates/arbitrary/src/foreign/core/ops.rs b/crates/arbitrary/src/foreign/core/ops.rs
new file mode 100644
index 0000000..8c24735
--- /dev/null
+++ b/crates/arbitrary/src/foreign/core/ops.rs
@@ -0,0 +1,127 @@
+use {
+    crate::{size_hint, Arbitrary, MaxRecursionReached, Result, Unstructured},
+    core::{
+        mem,
+        ops::{Bound, Range, RangeBounds, RangeFrom, RangeInclusive, RangeTo, RangeToInclusive},
+    },
+};
+
+macro_rules! impl_range {
+    (
+        $range:ty,
+        $value_closure:expr,
+        $value_ty:ty,
+        $fun:ident($fun_closure:expr),
+        $size_hint_closure:expr
+    ) => {
+        impl<'a, A> Arbitrary<'a> for $range
+        where
+            A: Arbitrary<'a> + Clone + PartialOrd,
+        {
+            fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+                let value: $value_ty = Arbitrary::arbitrary(u)?;
+                Ok($fun(value, $fun_closure))
+            }
+
+            #[inline]
+            fn size_hint(depth: usize) -> (usize, Option<usize>) {
+                Self::try_size_hint(depth).unwrap_or_default()
+            }
+
+            #[inline]
+            fn try_size_hint(depth: usize) -> Result<(usize, Option<usize>), MaxRecursionReached> {
+                #[allow(clippy::redundant_closure_call)]
+                $size_hint_closure(depth)
+            }
+        }
+    };
+}
+impl_range!(
+    Range<A>,
+    |r: &Range<A>| (r.start.clone(), r.end.clone()),
+    (A, A),
+    bounded_range(|(a, b)| a..b),
+    |depth| Ok(crate::size_hint::and(
+        <A as Arbitrary>::try_size_hint(depth)?,
+        <A as Arbitrary>::try_size_hint(depth)?,
+    ))
+);
+impl_range!(
+    RangeFrom<A>,
+    |r: &RangeFrom<A>| r.start.clone(),
+    A,
+    unbounded_range(|a| a..),
+    |depth| <A as Arbitrary>::try_size_hint(depth)
+);
+impl_range!(
+    RangeInclusive<A>,
+    |r: &RangeInclusive<A>| (r.start().clone(), r.end().clone()),
+    (A, A),
+    bounded_range(|(a, b)| a..=b),
+    |depth| Ok(crate::size_hint::and(
+        <A as Arbitrary>::try_size_hint(depth)?,
+        <A as Arbitrary>::try_size_hint(depth)?,
+    ))
+);
+impl_range!(
+    RangeTo<A>,
+    |r: &RangeTo<A>| r.end.clone(),
+    A,
+    unbounded_range(|b| ..b),
+    |depth| <A as Arbitrary>::try_size_hint(depth)
+);
+impl_range!(
+    RangeToInclusive<A>,
+    |r: &RangeToInclusive<A>| r.end.clone(),
+    A,
+    unbounded_range(|b| ..=b),
+    |depth| <A as Arbitrary>::try_size_hint(depth)
+);
+
+pub(crate) fn bounded_range<CB, I, R>(bounds: (I, I), cb: CB) -> R
+where
+    CB: Fn((I, I)) -> R,
+    I: PartialOrd,
+    R: RangeBounds<I>,
+{
+    let (mut start, mut end) = bounds;
+    if start > end {
+        mem::swap(&mut start, &mut end);
+    }
+    cb((start, end))
+}
+
+pub(crate) fn unbounded_range<CB, I, R>(bound: I, cb: CB) -> R
+where
+    CB: Fn(I) -> R,
+    R: RangeBounds<I>,
+{
+    cb(bound)
+}
+
+impl<'a, A> Arbitrary<'a> for Bound<A>
+where
+    A: Arbitrary<'a>,
+{
+    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+        match u.int_in_range::<u8>(0..=2)? {
+            0 => Ok(Bound::Included(A::arbitrary(u)?)),
+            1 => Ok(Bound::Excluded(A::arbitrary(u)?)),
+            2 => Ok(Bound::Unbounded),
+            _ => unreachable!(),
+        }
+    }
+
+    #[inline]
+    fn size_hint(depth: usize) -> (usize, Option<usize>) {
+        Self::try_size_hint(depth).unwrap_or_default()
+    }
+
+    #[inline]
+    fn try_size_hint(depth: usize) -> Result<(usize, Option<usize>), MaxRecursionReached> {
+        Ok(size_hint::or(
+            size_hint::and((1, Some(1)), A::try_size_hint(depth)?),
+            (1, Some(1)),
+        ))
+    }
+}
diff --git a/crates/arbitrary/src/foreign/core/option.rs b/crates/arbitrary/src/foreign/core/option.rs
new file mode 100644
index 0000000..76ab978
--- /dev/null
+++ b/crates/arbitrary/src/foreign/core/option.rs
@@ -0,0 +1,27 @@
+use crate::{size_hint, Arbitrary, MaxRecursionReached, Result, Unstructured};
+
+impl<'a, A> Arbitrary<'a> for Option<A>
+where
+    A: Arbitrary<'a>,
+{
+    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+        Ok(if <bool as Arbitrary<'a>>::arbitrary(u)? {
+            Some(Arbitrary::arbitrary(u)?)
+        } else {
+            None
+        })
+    }
+
+    #[inline]
+    fn size_hint(depth: usize) -> (usize, Option<usize>) {
+        Self::try_size_hint(depth).unwrap_or_default()
+    }
+
+    #[inline]
+    fn try_size_hint(depth: usize) -> Result<(usize, Option<usize>), MaxRecursionReached> {
+        Ok(size_hint::and(
+            <bool as Arbitrary>::try_size_hint(depth)?,
+            size_hint::or((0, Some(0)), <A as Arbitrary>::try_size_hint(depth)?),
+        ))
+    }
+}
diff --git a/crates/arbitrary/src/foreign/core/result.rs b/crates/arbitrary/src/foreign/core/result.rs
new file mode 100644
index 0000000..65ad50a
--- /dev/null
+++ b/crates/arbitrary/src/foreign/core/result.rs
@@ -0,0 +1,31 @@
+use crate::{size_hint, Arbitrary, Error, MaxRecursionReached, Unstructured};
+
+impl<'a, T, E> Arbitrary<'a> for Result<T, E>
+where
+    T: Arbitrary<'a>,
+    E: Arbitrary<'a>,
+{
+    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self, Error> {
+        Ok(if <bool as Arbitrary<'a>>::arbitrary(u)? {
+            Ok(<T as Arbitrary>::arbitrary(u)?)
+        } else {
+            Err(<E as Arbitrary>::arbitrary(u)?)
+        })
+    }
+
+    #[inline]
+    fn size_hint(depth: usize) -> (usize, Option<usize>) {
+        Self::try_size_hint(depth).unwrap_or_default()
+    }
+
+    #[inline]
+    fn try_size_hint(depth: usize) -> Result<(usize, Option<usize>), MaxRecursionReached> {
+        Ok(size_hint::and(
+            <bool as Arbitrary>::size_hint(depth),
+            size_hint::or(
+                <T as Arbitrary>::try_size_hint(depth)?,
+                <E as Arbitrary>::try_size_hint(depth)?,
+            ),
+        ))
+    }
+}
diff --git a/crates/arbitrary/src/foreign/core/slice.rs b/crates/arbitrary/src/foreign/core/slice.rs
new file mode 100644
index 0000000..296d474
--- /dev/null
+++ b/crates/arbitrary/src/foreign/core/slice.rs
@@ -0,0 +1,17 @@
+use crate::{Arbitrary, Result, Unstructured};
+
+impl<'a> Arbitrary<'a> for &'a [u8] {
+    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+        let len = u.arbitrary_len::<u8>()?;
+        u.bytes(len)
+    }
+
+    fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self> {
+        Ok(u.take_rest())
+    }
+
+    #[inline]
+    fn size_hint(_depth: usize) -> (usize, Option<usize>) {
+        (0, None)
+    }
+}
diff --git a/crates/arbitrary/src/foreign/core/str.rs b/crates/arbitrary/src/foreign/core/str.rs
new file mode 100644
index 0000000..cfca8ad
--- /dev/null
+++ b/crates/arbitrary/src/foreign/core/str.rs
@@ -0,0 +1,39 @@
+use {
+    crate::{Arbitrary, Result, Unstructured},
+    core::str,
+};
+
+fn arbitrary_str<'a>(u: &mut Unstructured<'a>, size: usize) -> Result<&'a str> {
+    match str::from_utf8(u.peek_bytes(size).unwrap()) {
+        Ok(s) => {
+            u.bytes(size).unwrap();
+            Ok(s)
+        }
+        Err(e) => {
+            let i = e.valid_up_to();
+            let valid = u.bytes(i).unwrap();
+            let s = unsafe {
+                debug_assert!(str::from_utf8(valid).is_ok());
+                str::from_utf8_unchecked(valid)
+            };
+            Ok(s)
+        }
+    }
+}
+
+impl<'a> Arbitrary<'a> for &'a str {
+    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+        let size = u.arbitrary_len::<u8>()?;
+        arbitrary_str(u, size)
+    }
+
+    fn arbitrary_take_rest(mut u: Unstructured<'a>) -> Result<Self> {
+        let size = u.len();
+        arbitrary_str(&mut u, size)
+    }
+
+    #[inline]
+    fn size_hint(_depth: usize) -> (usize, Option<usize>) {
+        (0, None)
+    }
+}
diff --git a/crates/arbitrary/src/foreign/core/sync/atomic.rs b/crates/arbitrary/src/foreign/core/sync/atomic.rs
new file mode 100644
index 0000000..004c396
--- /dev/null
+++ b/crates/arbitrary/src/foreign/core/sync/atomic.rs
@@ -0,0 +1,37 @@
+use {
+    crate::{Arbitrary, Result, Unstructured},
+    core::sync::atomic::{AtomicBool, AtomicIsize, AtomicUsize},
+};
+
+impl<'a> Arbitrary<'a> for AtomicBool {
+    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+        Arbitrary::arbitrary(u).map(Self::new)
+    }
+
+    #[inline]
+    fn size_hint(depth: usize) -> (usize, Option<usize>) {
+        <bool as Arbitrary<'a>>::size_hint(depth)
+    }
+}
+
+impl<'a> Arbitrary<'a> for AtomicIsize {
+    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+        Arbitrary::arbitrary(u).map(Self::new)
+    }
+
+    #[inline]
+    fn size_hint(depth: usize) -> (usize, Option<usize>) {
+        <isize as Arbitrary<'a>>::size_hint(depth)
+    }
+}
+
+impl<'a> Arbitrary<'a> for AtomicUsize {
+    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+        Arbitrary::arbitrary(u).map(Self::new)
+    }
+
+    #[inline]
+    fn size_hint(depth: usize) -> (usize, Option<usize>) {
+        <usize as Arbitrary<'a>>::size_hint(depth)
+    }
+}
diff --git a/crates/arbitrary/src/foreign/core/sync/mod.rs b/crates/arbitrary/src/foreign/core/sync/mod.rs
new file mode 100644
index 0000000..f7b1416
--- /dev/null
+++ b/crates/arbitrary/src/foreign/core/sync/mod.rs
@@ -0,0 +1 @@
+mod atomic;
diff --git a/crates/arbitrary/src/foreign/core/time.rs b/crates/arbitrary/src/foreign/core/time.rs
new file mode 100644
index 0000000..9ab3434
--- /dev/null
+++ b/crates/arbitrary/src/foreign/core/time.rs
@@ -0,0 +1,21 @@
+use {
+    crate::{size_hint, Arbitrary, Result, Unstructured},
+    core::time::Duration,
+};
+
+impl<'a> Arbitrary<'a> for Duration {
+    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+        Ok(Self::new(
+            <u64 as Arbitrary>::arbitrary(u)?,
+            u.int_in_range(0..=999_999_999)?,
+        ))
+    }
+
+    #[inline]
+    fn size_hint(depth: usize) -> (usize, Option<usize>) {
+        size_hint::and(
+            <u64 as Arbitrary>::size_hint(depth),
+            <u32 as Arbitrary>::size_hint(depth),
+        )
+    }
+}
diff --git a/crates/arbitrary/src/foreign/core/tuple.rs b/crates/arbitrary/src/foreign/core/tuple.rs
new file mode 100644
index 0000000..dfdd469
--- /dev/null
+++ b/crates/arbitrary/src/foreign/core/tuple.rs
@@ -0,0 +1,38 @@
+use crate::{size_hint, Arbitrary, MaxRecursionReached, Result, Unstructured};
+
+macro_rules! arbitrary_tuple {
+    () => {};
+    ($last: ident $($xs: ident)*) => {
+        arbitrary_tuple!($($xs)*);
+
+        impl<'a, $($xs,)* $last> Arbitrary<'a> for ($($xs,)* $last,)
+        where
+            $($xs: Arbitrary<'a>,)*
+            $last: Arbitrary<'a>,
+        {
+            fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+                Ok(($($xs::arbitrary(u)?,)* Arbitrary::arbitrary(u)?,))
+            }
+
+            #[allow(unused_mut, non_snake_case)]
+            fn arbitrary_take_rest(mut u: Unstructured<'a>) -> Result<Self> {
+                $(let $xs = $xs::arbitrary(&mut u)?;)*
+                let $last = $last::arbitrary_take_rest(u)?;
+                Ok(($($xs,)* $last,))
+            }
+
+            #[inline]
+            fn size_hint(depth: usize) -> (usize, Option<usize>) {
+                Self::try_size_hint(depth).unwrap_or_default()
+            }
+            #[inline]
+            fn try_size_hint(depth: usize) -> Result<(usize, Option<usize>), MaxRecursionReached> {
+                Ok(size_hint::and_all(&[
+                    <$last as Arbitrary>::try_size_hint(depth)?,
+                    $( <$xs as Arbitrary>::try_size_hint(depth)?),*
+                ]))
+            }
+        }
+    };
+}
+arbitrary_tuple!(A B C D E F G H I J K L M N O P Q R S T U V W X Y Z);
diff --git a/crates/arbitrary/src/foreign/core/unit.rs b/crates/arbitrary/src/foreign/core/unit.rs
new file mode 100644
index 0000000..20523a6
--- /dev/null
+++ b/crates/arbitrary/src/foreign/core/unit.rs
@@ -0,0 +1,12 @@
+use crate::{Arbitrary, Result, Unstructured};
+
+impl<'a> Arbitrary<'a> for () {
+    fn arbitrary(_: &mut Unstructured<'a>) -> Result<Self> {
+        Ok(())
+    }
+
+    #[inline]
+    fn size_hint(_depth: usize) -> (usize, Option<usize>) {
+        (0, Some(0))
+    }
+}
diff --git a/crates/arbitrary/src/foreign/mod.rs b/crates/arbitrary/src/foreign/mod.rs
new file mode 100644
index 0000000..b1c42be
--- /dev/null
+++ b/crates/arbitrary/src/foreign/mod.rs
@@ -0,0 +1,7 @@
+//! Implementations of [`Arbitrary`] for foreign types.
+//!
+//! [`Arbitrary`]: crate::Arbitrary
+
+mod alloc;
+mod core;
+mod std;
diff --git a/crates/arbitrary/src/foreign/std/collections/hash_map.rs b/crates/arbitrary/src/foreign/std/collections/hash_map.rs
new file mode 100644
index 0000000..d2e77af
--- /dev/null
+++ b/crates/arbitrary/src/foreign/std/collections/hash_map.rs
@@ -0,0 +1,27 @@
+use {
+    crate::{Arbitrary, Result, Unstructured},
+    std::{
+        collections::hash_map::HashMap,
+        hash::{BuildHasher, Hash},
+    },
+};
+
+impl<'a, K, V, S> Arbitrary<'a> for HashMap<K, V, S>
+where
+    K: Arbitrary<'a> + Eq + Hash,
+    V: Arbitrary<'a>,
+    S: BuildHasher + Default,
+{
+    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+        u.arbitrary_iter()?.collect()
+    }
+
+    fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self> {
+        u.arbitrary_take_rest_iter()?.collect()
+    }
+
+    #[inline]
+    fn size_hint(_depth: usize) -> (usize, Option<usize>) {
+        (0, None)
+    }
+}
diff --git a/crates/arbitrary/src/foreign/std/collections/hash_set.rs b/crates/arbitrary/src/foreign/std/collections/hash_set.rs
new file mode 100644
index 0000000..5cb63d2
--- /dev/null
+++ b/crates/arbitrary/src/foreign/std/collections/hash_set.rs
@@ -0,0 +1,26 @@
+use {
+    crate::{Arbitrary, Result, Unstructured},
+    std::{
+        collections::hash_set::HashSet,
+        hash::{BuildHasher, Hash},
+    },
+};
+
+impl<'a, A, S> Arbitrary<'a> for HashSet<A, S>
+where
+    A: Arbitrary<'a> + Eq + Hash,
+    S: BuildHasher + Default,
+{
+    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+        u.arbitrary_iter()?.collect()
+    }
+
+    fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self> {
+        u.arbitrary_take_rest_iter()?.collect()
+    }
+
+    #[inline]
+    fn size_hint(_depth: usize) -> (usize, Option<usize>) {
+        (0, None)
+    }
+}
diff --git a/crates/arbitrary/src/foreign/std/collections/mod.rs b/crates/arbitrary/src/foreign/std/collections/mod.rs
new file mode 100644
index 0000000..2bde6a0
--- /dev/null
+++ b/crates/arbitrary/src/foreign/std/collections/mod.rs
@@ -0,0 +1,2 @@
+mod hash_map;
+mod hash_set;
diff --git a/crates/arbitrary/src/foreign/std/ffi/c_str.rs b/crates/arbitrary/src/foreign/std/ffi/c_str.rs
new file mode 100644
index 0000000..7a1dc29
--- /dev/null
+++ b/crates/arbitrary/src/foreign/std/ffi/c_str.rs
@@ -0,0 +1,5 @@
+// impl Arbitrary for Box<CStr> {
+//     fn arbitrary(u: &mut Unstructured<'_>) -> Result<Self> {
+//         <CString as Arbitrary>::arbitrary(u).map(|x| x.into_boxed_c_str())
+//     }
+// }
diff --git a/crates/arbitrary/src/foreign/std/ffi/mod.rs b/crates/arbitrary/src/foreign/std/ffi/mod.rs
new file mode 100644
index 0000000..f5d739f
--- /dev/null
+++ b/crates/arbitrary/src/foreign/std/ffi/mod.rs
@@ -0,0 +1,2 @@
+mod c_str;
+mod os_str;
diff --git a/crates/arbitrary/src/foreign/std/ffi/os_str.rs b/crates/arbitrary/src/foreign/std/ffi/os_str.rs
new file mode 100644
index 0000000..9fa0cb3
--- /dev/null
+++ b/crates/arbitrary/src/foreign/std/ffi/os_str.rs
@@ -0,0 +1,22 @@
+use {
+    crate::{Arbitrary, Result, Unstructured},
+    std::ffi::OsString,
+};
+
+impl<'a> Arbitrary<'a> for OsString {
+    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+        <String as Arbitrary>::arbitrary(u).map(From::from)
+    }
+
+    #[inline]
+    fn size_hint(depth: usize) -> (usize, Option<usize>) {
+        <String as Arbitrary>::size_hint(depth)
+    }
+}
+
+// impl Arbitrary for Box<OsStr> {
+//     fn arbitrary(u: &mut Unstructured<'_>) -> Result<Self> {
+//         <OsString as Arbitrary>::arbitrary(u).map(|x| x.into_boxed_osstr())
+//
+//     }
+// }
diff --git a/crates/arbitrary/src/foreign/std/mod.rs b/crates/arbitrary/src/foreign/std/mod.rs
new file mode 100644
index 0000000..bc85aa2
--- /dev/null
+++ b/crates/arbitrary/src/foreign/std/mod.rs
@@ -0,0 +1,10 @@
+//! Implementations of [`Arbitrary`] for [`std`] types,
+//!   excluding those in [`core`] and [`alloc`].
+//!
+//! [`Arbitrary`]: crate::Arbitrary
+
+mod collections;
+mod ffi;
+mod net;
+mod path;
+mod sync;
diff --git a/crates/arbitrary/src/foreign/std/net.rs b/crates/arbitrary/src/foreign/std/net.rs
new file mode 100644
index 0000000..41e1f8a
--- /dev/null
+++ b/crates/arbitrary/src/foreign/std/net.rs
@@ -0,0 +1,96 @@
+use {
+    crate::{size_hint, Arbitrary, Result, Unstructured},
+    std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6},
+};
+
+impl<'a> Arbitrary<'a> for Ipv4Addr {
+    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+        Ok(Ipv4Addr::from(u32::arbitrary(u)?))
+    }
+
+    #[inline]
+    fn size_hint(_depth: usize) -> (usize, Option<usize>) {
+        (4, Some(4))
+    }
+}
+
+impl<'a> Arbitrary<'a> for Ipv6Addr {
+    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+        Ok(Ipv6Addr::from(u128::arbitrary(u)?))
+    }
+
+    #[inline]
+    fn size_hint(_depth: usize) -> (usize, Option<usize>) {
+        (16, Some(16))
+    }
+}
+
+impl<'a> Arbitrary<'a> for IpAddr {
+    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+        if u.arbitrary()? {
+            Ok(IpAddr::V4(u.arbitrary()?))
+        } else {
+            Ok(IpAddr::V6(u.arbitrary()?))
+        }
+    }
+
+    fn size_hint(depth: usize) -> (usize, Option<usize>) {
+        size_hint::and(
+            bool::size_hint(depth),
+            size_hint::or(Ipv4Addr::size_hint(depth), Ipv6Addr::size_hint(depth)),
+        )
+    }
+}
+
+impl<'a> Arbitrary<'a> for SocketAddrV4 {
+    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+        Ok(SocketAddrV4::new(u.arbitrary()?, u.arbitrary()?))
+    }
+
+    #[inline]
+    fn size_hint(depth: usize) -> (usize, Option<usize>) {
+        size_hint::and(Ipv4Addr::size_hint(depth), u16::size_hint(depth))
+    }
+}
+
+impl<'a> Arbitrary<'a> for SocketAddrV6 {
+    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+        Ok(SocketAddrV6::new(
+            u.arbitrary()?,
+            u.arbitrary()?,
+            u.arbitrary()?,
+            u.arbitrary()?,
+        ))
+    }
+
+    #[inline]
+    fn size_hint(depth: usize) -> (usize, Option<usize>) {
+        size_hint::and(
+            Ipv6Addr::size_hint(depth),
+            size_hint::and(
+                u16::size_hint(depth),
+                size_hint::and(u32::size_hint(depth), u32::size_hint(depth)),
+            ),
+        )
+    }
+}
+
+impl<'a> Arbitrary<'a> for SocketAddr {
+    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+        if u.arbitrary()? {
+            Ok(SocketAddr::V4(u.arbitrary()?))
+        } else {
+            Ok(SocketAddr::V6(u.arbitrary()?))
+        }
+    }
+
+    fn size_hint(depth: usize) -> (usize, Option<usize>) {
+        size_hint::and(
+            bool::size_hint(depth),
+            size_hint::or(
+                SocketAddrV4::size_hint(depth),
+                SocketAddrV6::size_hint(depth),
+            ),
+        )
+    }
+}
diff --git a/crates/arbitrary/src/foreign/std/path.rs b/crates/arbitrary/src/foreign/std/path.rs
new file mode 100644
index 0000000..c94797b
--- /dev/null
+++ b/crates/arbitrary/src/foreign/std/path.rs
@@ -0,0 +1,15 @@
+use {
+    crate::{Arbitrary, Result, Unstructured},
+    std::{ffi::OsString, path::PathBuf},
+};
+
+impl<'a> Arbitrary<'a> for PathBuf {
+    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+        <OsString as Arbitrary>::arbitrary(u).map(From::from)
+    }
+
+    #[inline]
+    fn size_hint(depth: usize) -> (usize, Option<usize>) {
+        <OsString as Arbitrary>::size_hint(depth)
+    }
+}
diff --git a/crates/arbitrary/src/foreign/std/sync.rs b/crates/arbitrary/src/foreign/std/sync.rs
new file mode 100644
index 0000000..312b37e
--- /dev/null
+++ b/crates/arbitrary/src/foreign/std/sync.rs
@@ -0,0 +1,23 @@
+use {
+    crate::{Arbitrary, MaxRecursionReached, Result, Unstructured},
+    std::sync::Mutex,
+};
+
+impl<'a, A> Arbitrary<'a> for Mutex<A>
+where
+    A: Arbitrary<'a>,
+{
+    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+        Arbitrary::arbitrary(u).map(Self::new)
+    }
+
+    #[inline]
+    fn size_hint(depth: usize) -> (usize, Option<usize>) {
+        Self::try_size_hint(depth).unwrap_or_default()
+    }
+
+    #[inline]
+    fn try_size_hint(depth: usize) -> Result<(usize, Option<usize>), MaxRecursionReached> {
+        A::try_size_hint(depth)
+    }
+}
diff --git a/crates/arbitrary/src/lib.rs b/crates/arbitrary/src/lib.rs
index a3fa48b..2535d3a 100644
--- a/crates/arbitrary/src/lib.rs
+++ b/crates/arbitrary/src/lib.rs
@@ -8,10 +8,10 @@
 
 //! The `Arbitrary` trait crate.
 //!
-//! This trait provides an [`Arbitrary`](./trait.Arbitrary.html) trait to
+//! This trait provides an [`Arbitrary`] trait to
 //! produce well-typed, structured values, from raw, byte buffers. It is
 //! generally intended to be used with fuzzers like AFL or libFuzzer. See the
-//! [`Arbitrary`](./trait.Arbitrary.html) trait's documentation for details on
+//! [`Arbitrary`] trait's documentation for details on
 //! automatically deriving, implementing, and/or using the trait.
 
 #![deny(bad_style)]
@@ -22,37 +22,34 @@
 #![deny(rust_2018_idioms)]
 #![deny(unused)]
 
+mod error;
+mod foreign;
+pub mod size_hint;
+pub mod unstructured;
+
+#[cfg(test)]
+mod tests;
+
+pub use error::*;
+
 #[cfg(feature = "derive_arbitrary")]
 pub use derive_arbitrary::*;
 
-mod error;
-pub use error::*;
-
-pub mod unstructured;
 #[doc(inline)]
 pub use unstructured::Unstructured;
 
-pub mod size_hint;
+/// Error indicating that the maximum recursion depth has been reached while calculating [`Arbitrary::size_hint`]()
+#[derive(Debug, Clone)]
+#[non_exhaustive]
+pub struct MaxRecursionReached {}
 
-use core::array;
-use core::cell::{Cell, RefCell, UnsafeCell};
-use core::iter;
-use core::mem;
-use core::num::{NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize};
-use core::num::{NonZeroU128, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize};
-use core::ops::{Range, RangeBounds, RangeFrom, RangeInclusive, RangeTo, RangeToInclusive};
-use core::str;
-use core::time::Duration;
-use std::borrow::{Cow, ToOwned};
-use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque};
-use std::ffi::{CString, OsString};
-use std::hash::BuildHasher;
-use std::net::{Ipv4Addr, Ipv6Addr};
-use std::ops::Bound;
-use std::path::PathBuf;
-use std::rc::Rc;
-use std::sync::atomic::{AtomicBool, AtomicIsize, AtomicUsize};
-use std::sync::{Arc, Mutex};
+impl core::fmt::Display for MaxRecursionReached {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        f.write_str("Maximum recursion depth has been reached")
+    }
+}
+
+impl std::error::Error for MaxRecursionReached {}
 
 /// Generate arbitrary structured values from raw, unstructured data.
 ///
@@ -96,13 +93,32 @@
 ///
 /// Every member of the `struct` or `enum` must also implement `Arbitrary`.
 ///
+/// It is also possible to change the default bounds added by the derive:
+///
+/// ```
+/// # #[cfg(feature = "derive")] mod foo {
+/// use arbitrary::Arbitrary;
+///
+/// trait Trait {
+///     type Assoc: for<'a> Arbitrary<'a>;
+/// }
+///
+/// #[derive(Arbitrary)]
+/// // The bounds are used verbatim, so any existing trait bounds will need to be repeated.
+/// #[arbitrary(bound = "T: Trait")]
+/// struct Point<T: Trait> {
+///     x: T::Assoc,
+/// }
+/// # }
+/// ```
+///
 /// # Implementing `Arbitrary` By Hand
 ///
 /// Implementing `Arbitrary` mostly involves nested calls to other `Arbitrary`
 /// arbitrary implementations for each of your `struct` or `enum`'s members. But
 /// sometimes you need some amount of raw data, or you need to generate a
 /// variably-sized collection type, or something of that sort. The
-/// [`Unstructured`][crate::Unstructured] type helps you with these tasks.
+/// [`Unstructured`] type helps you with these tasks.
 ///
 /// ```
 /// # #[cfg(feature = "derive")] mod foo {
@@ -133,6 +149,17 @@
 /// }
 /// # }
 /// ```
+///
+/// # A Note On Output Distributions
+///
+/// There is no requirement for a particular distribution of the values. For
+/// example, it is not required that every value appears with the same
+/// probability. That being said, the main use for `Arbitrary` is for fuzzing,
+/// so in many cases a uniform distribution will make the most sense in order to
+/// provide the best coverage of the domain. In other cases this is not
+/// desirable or even possible, for example when sampling from a uniform
+/// distribution is computationally expensive or in the case of collections that
+/// may grow indefinitely.
 pub trait Arbitrary<'a>: Sized {
     /// Generate an arbitrary value of `Self` from the given unstructured data.
     ///
@@ -176,7 +203,7 @@
     /// # }
     /// ```
     ///
-    /// See also the documentation for [`Unstructured`][crate::Unstructured].
+    /// See also the documentation for [`Unstructured`].
     fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self>;
 
     /// Generate an arbitrary value of `Self` from the entirety of the given
@@ -185,7 +212,7 @@
     /// This is similar to Arbitrary::arbitrary, however it assumes that it is
     /// the last consumer of the given data, and is thus able to consume it all
     /// if it needs.  See also the documentation for
-    /// [`Unstructured`][crate::Unstructured].
+    /// [`Unstructured`].
     fn arbitrary_take_rest(mut u: Unstructured<'a>) -> Result<Self> {
         Self::arbitrary(&mut u)
     }
@@ -196,18 +223,85 @@
     /// This is useful for determining how many elements we should insert when
     /// creating an arbitrary collection.
     ///
-    /// The return value is similar to
-    /// [`Iterator::size_hint`][iterator-size-hint]: it returns a tuple where
-    /// the first element is a lower bound on the number of bytes required, and
-    /// the second element is an optional upper bound.
+    /// The return value is similar to [`Iterator::size_hint`]: it returns a
+    /// tuple where the first element is a lower bound on the number of bytes
+    /// required, and the second element is an optional upper bound.
     ///
     /// The default implementation return `(0, None)` which is correct for any
     /// type, but not ultimately that useful. Using `#[derive(Arbitrary)]` will
     /// create a better implementation. If you are writing an `Arbitrary`
     /// implementation by hand, and your type can be part of a dynamically sized
     /// collection (such as `Vec`), you are strongly encouraged to override this
-    /// default with a better implementation. The
-    /// [`size_hint`][crate::size_hint] module will help with this task.
+    /// default with a better implementation, and also override
+    /// [`try_size_hint`].
+    ///
+    /// ## How to implement this
+    ///
+    /// If the size hint calculation is a trivial constant and does not recurse
+    /// into any other `size_hint` call, you should implement it in `size_hint`:
+    ///
+    /// ```
+    /// use arbitrary::{size_hint, Arbitrary, Result, Unstructured};
+    ///
+    /// struct SomeStruct(u8);
+    ///
+    /// impl<'a> Arbitrary<'a> for SomeStruct {
+    ///     fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+    ///         let buf = &mut [0];
+    ///         u.fill_buffer(buf)?;
+    ///         Ok(SomeStruct(buf[0]))
+    ///     }
+    ///
+    ///     #[inline]
+    ///     fn size_hint(depth: usize) -> (usize, Option<usize>) {
+    ///         let _ = depth;
+    ///         (1, Some(1))
+    ///     }
+    /// }
+    /// ```
+    ///
+    /// Otherwise, it should instead be implemented in [`try_size_hint`],
+    /// and the `size_hint` implementation should forward to it:
+    ///
+    /// ```
+    /// use arbitrary::{size_hint, Arbitrary, MaxRecursionReached, Result, Unstructured};
+    ///
+    /// struct SomeStruct<A, B> {
+    ///     a: A,
+    ///     b: B,
+    /// }
+    ///
+    /// impl<'a, A: Arbitrary<'a>, B: Arbitrary<'a>> Arbitrary<'a> for SomeStruct<A, B> {
+    ///     fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+    ///         // ...
+    /// #       todo!()
+    ///     }
+    ///
+    ///     fn size_hint(depth: usize) -> (usize, Option<usize>) {
+    ///         // Return the value of try_size_hint
+    ///         //
+    ///         // If the recursion fails, return the default, always valid `(0, None)`
+    ///         Self::try_size_hint(depth).unwrap_or_default()
+    ///     }
+    ///
+    ///     fn try_size_hint(depth: usize) -> Result<(usize, Option<usize>), MaxRecursionReached> {
+    ///         // Protect against potential infinite recursion with
+    ///         // `try_recursion_guard`.
+    ///         size_hint::try_recursion_guard(depth, |depth| {
+    ///             // If we aren't too deep, then `recursion_guard` calls
+    ///             // this closure, which implements the natural size hint.
+    ///             // Don't forget to use the new `depth` in all nested
+    ///             // `try_size_hint` calls! We recommend shadowing the
+    ///             // parameter, like what is done here, so that you can't
+    ///             // accidentally use the wrong depth.
+    ///             Ok(size_hint::and(
+    ///                 <A as Arbitrary>::try_size_hint(depth)?,
+    ///                 <B as Arbitrary>::try_size_hint(depth)?,
+    ///             ))
+    ///         })
+    ///     }
+    /// }
+    /// ```
     ///
     /// ## Invariant
     ///
@@ -220,21 +314,67 @@
     /// outputs which are still possible to produce using the reduced input
     /// space.
     ///
-    /// ## The `depth` Parameter
+    /// [iterator-size-hint]: https://doc.rust-lang.org/stable/std/iter/trait.Iterator.html#method.size_hint
+    /// [`try_size_hint`]: Arbitrary::try_size_hint
+    #[inline]
+    fn size_hint(depth: usize) -> (usize, Option<usize>) {
+        let _ = depth;
+        (0, None)
+    }
+
+    /// Get a size hint for how many bytes out of an `Unstructured` this type
+    /// needs to construct itself.
+    ///
+    /// Unlike [`size_hint`], this function keeps the information that the
+    /// recursion limit was reached. This is required to "short circuit" the
+    /// calculation and avoid exponential blowup with recursive structures.
+    ///
+    /// If you are implementing [`size_hint`] for a struct that could be
+    /// recursive, you should implement `try_size_hint` and call the
+    /// `try_size_hint` when recursing
+    ///
+    ///
+    /// The return value is similar to [`core::iter::Iterator::size_hint`]: it
+    /// returns a tuple where the first element is a lower bound on the number
+    /// of bytes required, and the second element is an optional upper bound.
+    ///
+    /// The default implementation returns the value of [`size_hint`] which is
+    /// correct for any type, but might lead to exponential blowup when dealing
+    /// with recursive types.
+    ///
+    /// ## Invariant
+    ///
+    /// It must be possible to construct every possible output using only inputs
+    /// of lengths bounded by these parameters. This applies to both
+    /// [`Arbitrary::arbitrary`] and [`Arbitrary::arbitrary_take_rest`].
+    ///
+    /// This is trivially true for `(0, None)`. To restrict this further, it
+    /// must be proven that all inputs that are now excluded produced redundant
+    /// outputs which are still possible to produce using the reduced input
+    /// space.
+    ///
+    /// ## When to implement `try_size_hint`
     ///
     /// If you 100% know that the type you are implementing `Arbitrary` for is
     /// not a recursive type, or your implementation is not transitively calling
-    /// any other `size_hint` methods, you can ignore the `depth` parameter.
+    /// any other `size_hint` methods, you may implement [`size_hint`], and the
+    /// default `try_size_hint` implementation will use it.
+    ///
     /// Note that if you are implementing `Arbitrary` for a generic type, you
     /// cannot guarantee the lack of type recursion!
     ///
-    /// Otherwise, you need to use
-    /// [`arbitrary::size_hint::recursion_guard(depth)`][crate::size_hint::recursion_guard]
+    /// Otherwise, when there is possible type recursion, you should implement
+    /// `try_size_hint` instead.
+    ///
+    /// ## The `depth` parameter
+    ///
+    /// When implementing `try_size_hint`, you need to use
+    /// [`arbitrary::size_hint::try_recursion_guard(depth)`][crate::size_hint::try_recursion_guard]
     /// to prevent potential infinite recursion when calculating size hints for
     /// potentially recursive types:
     ///
     /// ```
-    /// use arbitrary::{Arbitrary, Unstructured, size_hint};
+    /// use arbitrary::{size_hint, Arbitrary, MaxRecursionReached, Unstructured};
     ///
     /// // This can potentially be a recursive type if `L` or `R` contain
     /// // something like `Box<Option<MyEither<L, R>>>`!
@@ -254,1042 +394,34 @@
     ///     }
     ///
     ///     fn size_hint(depth: usize) -> (usize, Option<usize>) {
+    ///         // Return the value of `try_size_hint`
+    ///         //
+    ///         // If the recursion fails, return the default `(0, None)` range,
+    ///         // which is always valid.
+    ///         Self::try_size_hint(depth).unwrap_or_default()
+    ///     }
+    ///
+    ///     fn try_size_hint(depth: usize) -> Result<(usize, Option<usize>), MaxRecursionReached> {
     ///         // Protect against potential infinite recursion with
-    ///         // `recursion_guard`.
-    ///         size_hint::recursion_guard(depth, |depth| {
+    ///         // `try_recursion_guard`.
+    ///         size_hint::try_recursion_guard(depth, |depth| {
     ///             // If we aren't too deep, then `recursion_guard` calls
     ///             // this closure, which implements the natural size hint.
     ///             // Don't forget to use the new `depth` in all nested
-    ///             // `size_hint` calls! We recommend shadowing the
+    ///             // `try_size_hint` calls! We recommend shadowing the
     ///             // parameter, like what is done here, so that you can't
     ///             // accidentally use the wrong depth.
-    ///             size_hint::or(
-    ///                 <L as Arbitrary>::size_hint(depth),
-    ///                 <R as Arbitrary>::size_hint(depth),
-    ///             )
+    ///             Ok(size_hint::or(
+    ///                 <L as Arbitrary>::try_size_hint(depth)?,
+    ///                 <R as Arbitrary>::try_size_hint(depth)?,
+    ///             ))
     ///         })
     ///     }
     /// }
     /// ```
-    ///
-    /// [iterator-size-hint]: https://doc.rust-lang.org/stable/std/iter/trait.Iterator.html#method.size_hint
     #[inline]
-    fn size_hint(depth: usize) -> (usize, Option<usize>) {
-        let _ = depth;
-        (0, None)
-    }
-}
-
-impl<'a> Arbitrary<'a> for () {
-    fn arbitrary(_: &mut Unstructured<'a>) -> Result<Self> {
-        Ok(())
-    }
-
-    #[inline]
-    fn size_hint(_depth: usize) -> (usize, Option<usize>) {
-        (0, Some(0))
-    }
-}
-
-impl<'a> Arbitrary<'a> for bool {
-    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
-        Ok(<u8 as Arbitrary<'a>>::arbitrary(u)? & 1 == 1)
-    }
-
-    #[inline]
-    fn size_hint(depth: usize) -> (usize, Option<usize>) {
-        <u8 as Arbitrary<'a>>::size_hint(depth)
-    }
-}
-
-macro_rules! impl_arbitrary_for_integers {
-    ( $( $ty:ty: $unsigned:ty; )* ) => {
-        $(
-            impl<'a> Arbitrary<'a> for $ty {
-                fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
-                    let mut buf = [0; mem::size_of::<$ty>()];
-                    u.fill_buffer(&mut buf)?;
-                    let mut x: $unsigned = 0;
-                    for i in 0..mem::size_of::<$ty>() {
-                        x |= buf[i] as $unsigned << (i * 8);
-                    }
-                    Ok(x as $ty)
-                }
-
-                #[inline]
-                fn size_hint(_depth: usize) -> (usize, Option<usize>) {
-                    let n = mem::size_of::<$ty>();
-                    (n, Some(n))
-                }
-
-            }
-        )*
-    }
-}
-
-impl_arbitrary_for_integers! {
-    u8: u8;
-    u16: u16;
-    u32: u32;
-    u64: u64;
-    u128: u128;
-    usize: usize;
-    i8: u8;
-    i16: u16;
-    i32: u32;
-    i64: u64;
-    i128: u128;
-    isize: usize;
-}
-
-macro_rules! impl_arbitrary_for_floats {
-    ( $( $ty:ident : $unsigned:ty; )* ) => {
-        $(
-            impl<'a> Arbitrary<'a> for $ty {
-                fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
-                    Ok(Self::from_bits(<$unsigned as Arbitrary<'a>>::arbitrary(u)?))
-                }
-
-                #[inline]
-                fn size_hint(depth: usize) -> (usize, Option<usize>) {
-                    <$unsigned as Arbitrary<'a>>::size_hint(depth)
-                }
-            }
-        )*
-    }
-}
-
-impl_arbitrary_for_floats! {
-    f32: u32;
-    f64: u64;
-}
-
-impl<'a> Arbitrary<'a> for char {
-    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
-        use std::char;
-        // The highest unicode code point is 0x11_FFFF
-        const CHAR_END: u32 = 0x11_0000;
-        // The size of the surrogate blocks
-        const SURROGATES_START: u32 = 0xD800;
-        let mut c = <u32 as Arbitrary<'a>>::arbitrary(u)? % CHAR_END;
-        if let Some(c) = char::from_u32(c) {
-            Ok(c)
-        } else {
-            // We found a surrogate, wrap and try again
-            c -= SURROGATES_START;
-            Ok(char::from_u32(c)
-                .expect("Generated character should be valid! This is a bug in arbitrary-rs"))
-        }
-    }
-
-    #[inline]
-    fn size_hint(depth: usize) -> (usize, Option<usize>) {
-        <u32 as Arbitrary<'a>>::size_hint(depth)
-    }
-}
-
-impl<'a> Arbitrary<'a> for AtomicBool {
-    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
-        Arbitrary::arbitrary(u).map(Self::new)
-    }
-
-    #[inline]
-    fn size_hint(depth: usize) -> (usize, Option<usize>) {
-        <bool as Arbitrary<'a>>::size_hint(depth)
-    }
-}
-
-impl<'a> Arbitrary<'a> for AtomicIsize {
-    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
-        Arbitrary::arbitrary(u).map(Self::new)
-    }
-
-    #[inline]
-    fn size_hint(depth: usize) -> (usize, Option<usize>) {
-        <isize as Arbitrary<'a>>::size_hint(depth)
-    }
-}
-
-impl<'a> Arbitrary<'a> for AtomicUsize {
-    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
-        Arbitrary::arbitrary(u).map(Self::new)
-    }
-
-    #[inline]
-    fn size_hint(depth: usize) -> (usize, Option<usize>) {
-        <usize as Arbitrary<'a>>::size_hint(depth)
-    }
-}
-
-macro_rules! impl_range {
-    (
-        $range:ty,
-        $value_closure:expr,
-        $value_ty:ty,
-        $fun:ident($fun_closure:expr),
-        $size_hint_closure:expr
-    ) => {
-        impl<'a, A> Arbitrary<'a> for $range
-        where
-            A: Arbitrary<'a> + Clone + PartialOrd,
-        {
-            fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
-                let value: $value_ty = Arbitrary::arbitrary(u)?;
-                Ok($fun(value, $fun_closure))
-            }
-
-            #[inline]
-            fn size_hint(depth: usize) -> (usize, Option<usize>) {
-                $size_hint_closure(depth)
-            }
-        }
-    };
-}
-
-impl_range!(
-    Range<A>,
-    |r: &Range<A>| (r.start.clone(), r.end.clone()),
-    (A, A),
-    bounded_range(|(a, b)| a..b),
-    |depth| crate::size_hint::and(
-        <A as Arbitrary>::size_hint(depth),
-        <A as Arbitrary>::size_hint(depth)
-    )
-);
-impl_range!(
-    RangeFrom<A>,
-    |r: &RangeFrom<A>| r.start.clone(),
-    A,
-    unbounded_range(|a| a..),
-    |depth| <A as Arbitrary>::size_hint(depth)
-);
-impl_range!(
-    RangeInclusive<A>,
-    |r: &RangeInclusive<A>| (r.start().clone(), r.end().clone()),
-    (A, A),
-    bounded_range(|(a, b)| a..=b),
-    |depth| crate::size_hint::and(
-        <A as Arbitrary>::size_hint(depth),
-        <A as Arbitrary>::size_hint(depth)
-    )
-);
-impl_range!(
-    RangeTo<A>,
-    |r: &RangeTo<A>| r.end.clone(),
-    A,
-    unbounded_range(|b| ..b),
-    |depth| <A as Arbitrary>::size_hint(depth)
-);
-impl_range!(
-    RangeToInclusive<A>,
-    |r: &RangeToInclusive<A>| r.end.clone(),
-    A,
-    unbounded_range(|b| ..=b),
-    |depth| <A as Arbitrary>::size_hint(depth)
-);
-
-pub(crate) fn bounded_range<CB, I, R>(bounds: (I, I), cb: CB) -> R
-where
-    CB: Fn((I, I)) -> R,
-    I: PartialOrd,
-    R: RangeBounds<I>,
-{
-    let (mut start, mut end) = bounds;
-    if start > end {
-        mem::swap(&mut start, &mut end);
-    }
-    cb((start, end))
-}
-
-pub(crate) fn unbounded_range<CB, I, R>(bound: I, cb: CB) -> R
-where
-    CB: Fn(I) -> R,
-    R: RangeBounds<I>,
-{
-    cb(bound)
-}
-
-impl<'a> Arbitrary<'a> for Duration {
-    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
-        Ok(Self::new(
-            <u64 as Arbitrary>::arbitrary(u)?,
-            u.int_in_range(0..=999_999_999)?,
-        ))
-    }
-
-    #[inline]
-    fn size_hint(depth: usize) -> (usize, Option<usize>) {
-        crate::size_hint::and(
-            <u64 as Arbitrary>::size_hint(depth),
-            <u32 as Arbitrary>::size_hint(depth),
-        )
-    }
-}
-
-impl<'a, A: Arbitrary<'a>> Arbitrary<'a> for Option<A> {
-    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
-        Ok(if <bool as Arbitrary<'a>>::arbitrary(u)? {
-            Some(Arbitrary::arbitrary(u)?)
-        } else {
-            None
-        })
-    }
-
-    #[inline]
-    fn size_hint(depth: usize) -> (usize, Option<usize>) {
-        crate::size_hint::and(
-            <bool as Arbitrary>::size_hint(depth),
-            crate::size_hint::or((0, Some(0)), <A as Arbitrary>::size_hint(depth)),
-        )
-    }
-}
-
-impl<'a, A: Arbitrary<'a>, B: Arbitrary<'a>> Arbitrary<'a> for std::result::Result<A, B> {
-    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
-        Ok(if <bool as Arbitrary<'a>>::arbitrary(u)? {
-            Ok(<A as Arbitrary>::arbitrary(u)?)
-        } else {
-            Err(<B as Arbitrary>::arbitrary(u)?)
-        })
-    }
-
-    #[inline]
-    fn size_hint(depth: usize) -> (usize, Option<usize>) {
-        crate::size_hint::and(
-            <bool as Arbitrary>::size_hint(depth),
-            crate::size_hint::or(
-                <A as Arbitrary>::size_hint(depth),
-                <B as Arbitrary>::size_hint(depth),
-            ),
-        )
-    }
-}
-
-macro_rules! arbitrary_tuple {
-    () => {};
-    ($last: ident $($xs: ident)*) => {
-        arbitrary_tuple!($($xs)*);
-
-        impl<'a, $($xs: Arbitrary<'a>,)* $last: Arbitrary<'a>> Arbitrary<'a> for ($($xs,)* $last,) {
-            fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
-                Ok(($($xs::arbitrary(u)?,)* Arbitrary::arbitrary(u)?,))
-            }
-
-            #[allow(unused_mut, non_snake_case)]
-            fn arbitrary_take_rest(mut u: Unstructured<'a>) -> Result<Self> {
-                $(let $xs = $xs::arbitrary(&mut u)?;)*
-                let $last = $last::arbitrary_take_rest(u)?;
-                Ok(($($xs,)* $last,))
-            }
-
-            #[inline]
-            fn size_hint(depth: usize) -> (usize, Option<usize>) {
-                crate::size_hint::and_all(&[
-                    <$last as Arbitrary>::size_hint(depth),
-                    $( <$xs as Arbitrary>::size_hint(depth) ),*
-                ])
-            }
-        }
-    };
-}
-arbitrary_tuple!(A B C D E F G H I J K L M N O P Q R S T U V W X Y Z);
-
-// Helper to safely create arrays since the standard library doesn't
-// provide one yet. Shouldn't be necessary in the future.
-struct ArrayGuard<T, const N: usize> {
-    dst: *mut T,
-    initialized: usize,
-}
-
-impl<T, const N: usize> Drop for ArrayGuard<T, N> {
-    fn drop(&mut self) {
-        debug_assert!(self.initialized <= N);
-        let initialized_part = core::ptr::slice_from_raw_parts_mut(self.dst, self.initialized);
-        unsafe {
-            core::ptr::drop_in_place(initialized_part);
-        }
-    }
-}
-
-fn try_create_array<F, T, const N: usize>(mut cb: F) -> Result<[T; N]>
-where
-    F: FnMut(usize) -> Result<T>,
-{
-    let mut array: mem::MaybeUninit<[T; N]> = mem::MaybeUninit::uninit();
-    let array_ptr = array.as_mut_ptr();
-    let dst = array_ptr as _;
-    let mut guard: ArrayGuard<T, N> = ArrayGuard {
-        dst,
-        initialized: 0,
-    };
-    unsafe {
-        for (idx, value_ptr) in (*array.as_mut_ptr()).iter_mut().enumerate() {
-            core::ptr::write(value_ptr, cb(idx)?);
-            guard.initialized += 1;
-        }
-        mem::forget(guard);
-        Ok(array.assume_init())
-    }
-}
-
-impl<'a, T, const N: usize> Arbitrary<'a> for [T; N]
-where
-    T: Arbitrary<'a>,
-{
-    #[inline]
-    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
-        try_create_array(|_| <T as Arbitrary<'a>>::arbitrary(u))
-    }
-
-    #[inline]
-    fn arbitrary_take_rest(mut u: Unstructured<'a>) -> Result<Self> {
-        let mut array = Self::arbitrary(&mut u)?;
-        if let Some(last) = array.last_mut() {
-            *last = Arbitrary::arbitrary_take_rest(u)?;
-        }
-        Ok(array)
-    }
-
-    #[inline]
-    fn size_hint(d: usize) -> (usize, Option<usize>) {
-        crate::size_hint::and_all(&array::from_fn::<_, N, _>(|_| {
-            <T as Arbitrary>::size_hint(d)
-        }))
-    }
-}
-
-impl<'a> Arbitrary<'a> for &'a [u8] {
-    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
-        let len = u.arbitrary_len::<u8>()?;
-        u.bytes(len)
-    }
-
-    fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self> {
-        Ok(u.take_rest())
-    }
-
-    #[inline]
-    fn size_hint(_depth: usize) -> (usize, Option<usize>) {
-        (0, None)
-    }
-}
-
-impl<'a, A: Arbitrary<'a>> Arbitrary<'a> for Vec<A> {
-    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
-        u.arbitrary_iter()?.collect()
-    }
-
-    fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self> {
-        u.arbitrary_take_rest_iter()?.collect()
-    }
-
-    #[inline]
-    fn size_hint(_depth: usize) -> (usize, Option<usize>) {
-        (0, None)
-    }
-}
-
-impl<'a, K: Arbitrary<'a> + Ord, V: Arbitrary<'a>> Arbitrary<'a> for BTreeMap<K, V> {
-    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
-        u.arbitrary_iter()?.collect()
-    }
-
-    fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self> {
-        u.arbitrary_take_rest_iter()?.collect()
-    }
-
-    #[inline]
-    fn size_hint(_depth: usize) -> (usize, Option<usize>) {
-        (0, None)
-    }
-}
-
-impl<'a, A: Arbitrary<'a> + Ord> Arbitrary<'a> for BTreeSet<A> {
-    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
-        u.arbitrary_iter()?.collect()
-    }
-
-    fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self> {
-        u.arbitrary_take_rest_iter()?.collect()
-    }
-
-    #[inline]
-    fn size_hint(_depth: usize) -> (usize, Option<usize>) {
-        (0, None)
-    }
-}
-
-impl<'a, A: Arbitrary<'a>> Arbitrary<'a> for Bound<A> {
-    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
-        match u.int_in_range::<u8>(0..=2)? {
-            0 => Ok(Bound::Included(A::arbitrary(u)?)),
-            1 => Ok(Bound::Excluded(A::arbitrary(u)?)),
-            2 => Ok(Bound::Unbounded),
-            _ => unreachable!(),
-        }
-    }
-
-    #[inline]
-    fn size_hint(depth: usize) -> (usize, Option<usize>) {
-        size_hint::or(
-            size_hint::and((1, Some(1)), A::size_hint(depth)),
-            (1, Some(1)),
-        )
-    }
-}
-
-impl<'a, A: Arbitrary<'a> + Ord> Arbitrary<'a> for BinaryHeap<A> {
-    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
-        u.arbitrary_iter()?.collect()
-    }
-
-    fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self> {
-        u.arbitrary_take_rest_iter()?.collect()
-    }
-
-    #[inline]
-    fn size_hint(_depth: usize) -> (usize, Option<usize>) {
-        (0, None)
-    }
-}
-
-impl<'a, K: Arbitrary<'a> + Eq + ::std::hash::Hash, V: Arbitrary<'a>, S: BuildHasher + Default>
-    Arbitrary<'a> for HashMap<K, V, S>
-{
-    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
-        u.arbitrary_iter()?.collect()
-    }
-
-    fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self> {
-        u.arbitrary_take_rest_iter()?.collect()
-    }
-
-    #[inline]
-    fn size_hint(_depth: usize) -> (usize, Option<usize>) {
-        (0, None)
-    }
-}
-
-impl<'a, A: Arbitrary<'a> + Eq + ::std::hash::Hash, S: BuildHasher + Default> Arbitrary<'a>
-    for HashSet<A, S>
-{
-    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
-        u.arbitrary_iter()?.collect()
-    }
-
-    fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self> {
-        u.arbitrary_take_rest_iter()?.collect()
-    }
-
-    #[inline]
-    fn size_hint(_depth: usize) -> (usize, Option<usize>) {
-        (0, None)
-    }
-}
-
-impl<'a, A: Arbitrary<'a>> Arbitrary<'a> for LinkedList<A> {
-    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
-        u.arbitrary_iter()?.collect()
-    }
-
-    fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self> {
-        u.arbitrary_take_rest_iter()?.collect()
-    }
-
-    #[inline]
-    fn size_hint(_depth: usize) -> (usize, Option<usize>) {
-        (0, None)
-    }
-}
-
-impl<'a, A: Arbitrary<'a>> Arbitrary<'a> for VecDeque<A> {
-    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
-        u.arbitrary_iter()?.collect()
-    }
-
-    fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self> {
-        u.arbitrary_take_rest_iter()?.collect()
-    }
-
-    #[inline]
-    fn size_hint(_depth: usize) -> (usize, Option<usize>) {
-        (0, None)
-    }
-}
-
-impl<'a, A> Arbitrary<'a> for Cow<'a, A>
-where
-    A: ToOwned + ?Sized,
-    <A as ToOwned>::Owned: Arbitrary<'a>,
-{
-    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
-        Arbitrary::arbitrary(u).map(Cow::Owned)
-    }
-
-    #[inline]
-    fn size_hint(depth: usize) -> (usize, Option<usize>) {
-        crate::size_hint::recursion_guard(depth, |depth| {
-            <<A as ToOwned>::Owned as Arbitrary>::size_hint(depth)
-        })
-    }
-}
-
-impl<'a> Arbitrary<'a> for &'a str {
-    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
-        let size = u.arbitrary_len::<u8>()?;
-        match str::from_utf8(u.peek_bytes(size).unwrap()) {
-            Ok(s) => {
-                u.bytes(size).unwrap();
-                Ok(s)
-            }
-            Err(e) => {
-                let i = e.valid_up_to();
-                let valid = u.bytes(i).unwrap();
-                let s = unsafe {
-                    debug_assert!(str::from_utf8(valid).is_ok());
-                    str::from_utf8_unchecked(valid)
-                };
-                Ok(s)
-            }
-        }
-    }
-
-    fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self> {
-        let bytes = u.take_rest();
-        str::from_utf8(bytes).map_err(|_| Error::IncorrectFormat)
-    }
-
-    #[inline]
-    fn size_hint(_depth: usize) -> (usize, Option<usize>) {
-        (0, None)
-    }
-}
-
-impl<'a> Arbitrary<'a> for String {
-    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
-        <&str as Arbitrary>::arbitrary(u).map(Into::into)
-    }
-
-    fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self> {
-        <&str as Arbitrary>::arbitrary_take_rest(u).map(Into::into)
-    }
-
-    #[inline]
-    fn size_hint(depth: usize) -> (usize, Option<usize>) {
-        <&str as Arbitrary>::size_hint(depth)
-    }
-}
-
-impl<'a> Arbitrary<'a> for CString {
-    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
-        <Vec<u8> as Arbitrary>::arbitrary(u).map(|mut x| {
-            x.retain(|&c| c != 0);
-            Self::new(x).unwrap()
-        })
-    }
-
-    #[inline]
-    fn size_hint(depth: usize) -> (usize, Option<usize>) {
-        <Vec<u8> as Arbitrary>::size_hint(depth)
-    }
-}
-
-impl<'a> Arbitrary<'a> for OsString {
-    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
-        <String as Arbitrary>::arbitrary(u).map(From::from)
-    }
-
-    #[inline]
-    fn size_hint(depth: usize) -> (usize, Option<usize>) {
-        <String as Arbitrary>::size_hint(depth)
-    }
-}
-
-impl<'a> Arbitrary<'a> for PathBuf {
-    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
-        <OsString as Arbitrary>::arbitrary(u).map(From::from)
-    }
-
-    #[inline]
-    fn size_hint(depth: usize) -> (usize, Option<usize>) {
-        <OsString as Arbitrary>::size_hint(depth)
-    }
-}
-
-impl<'a, A: Arbitrary<'a>> Arbitrary<'a> for Box<A> {
-    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
-        Arbitrary::arbitrary(u).map(Self::new)
-    }
-
-    #[inline]
-    fn size_hint(depth: usize) -> (usize, Option<usize>) {
-        crate::size_hint::recursion_guard(depth, <A as Arbitrary>::size_hint)
-    }
-}
-
-impl<'a, A: Arbitrary<'a>> Arbitrary<'a> for Box<[A]> {
-    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
-        <Vec<A> as Arbitrary>::arbitrary(u).map(|x| x.into_boxed_slice())
-    }
-
-    #[inline]
-    fn size_hint(depth: usize) -> (usize, Option<usize>) {
-        <Vec<A> as Arbitrary>::size_hint(depth)
-    }
-}
-
-impl<'a> Arbitrary<'a> for Box<str> {
-    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
-        <String as Arbitrary>::arbitrary(u).map(|x| x.into_boxed_str())
-    }
-
-    #[inline]
-    fn size_hint(depth: usize) -> (usize, Option<usize>) {
-        <String as Arbitrary>::size_hint(depth)
-    }
-}
-
-// impl Arbitrary for Box<CStr> {
-//     fn arbitrary(u: &mut Unstructured<'_>) -> Result<Self> {
-//         <CString as Arbitrary>::arbitrary(u).map(|x| x.into_boxed_c_str())
-//     }
-// }
-
-// impl Arbitrary for Box<OsStr> {
-//     fn arbitrary(u: &mut Unstructured<'_>) -> Result<Self> {
-//         <OsString as Arbitrary>::arbitrary(u).map(|x| x.into_boxed_osstr())
-//
-//     }
-// }
-
-impl<'a, A: Arbitrary<'a>> Arbitrary<'a> for Arc<A> {
-    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
-        Arbitrary::arbitrary(u).map(Self::new)
-    }
-
-    #[inline]
-    fn size_hint(depth: usize) -> (usize, Option<usize>) {
-        crate::size_hint::recursion_guard(depth, <A as Arbitrary>::size_hint)
-    }
-}
-
-impl<'a> Arbitrary<'a> for Arc<str> {
-    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
-        <&str as Arbitrary>::arbitrary(u).map(Into::into)
-    }
-
-    #[inline]
-    fn size_hint(depth: usize) -> (usize, Option<usize>) {
-        <&str as Arbitrary>::size_hint(depth)
-    }
-}
-
-impl<'a, A: Arbitrary<'a>> Arbitrary<'a> for Rc<A> {
-    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
-        Arbitrary::arbitrary(u).map(Self::new)
-    }
-
-    #[inline]
-    fn size_hint(depth: usize) -> (usize, Option<usize>) {
-        crate::size_hint::recursion_guard(depth, <A as Arbitrary>::size_hint)
-    }
-}
-
-impl<'a> Arbitrary<'a> for Rc<str> {
-    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
-        <&str as Arbitrary>::arbitrary(u).map(Into::into)
-    }
-
-    #[inline]
-    fn size_hint(depth: usize) -> (usize, Option<usize>) {
-        <&str as Arbitrary>::size_hint(depth)
-    }
-}
-
-impl<'a, A: Arbitrary<'a>> Arbitrary<'a> for Cell<A> {
-    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
-        Arbitrary::arbitrary(u).map(Self::new)
-    }
-
-    #[inline]
-    fn size_hint(depth: usize) -> (usize, Option<usize>) {
-        <A as Arbitrary<'a>>::size_hint(depth)
-    }
-}
-
-impl<'a, A: Arbitrary<'a>> Arbitrary<'a> for RefCell<A> {
-    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
-        Arbitrary::arbitrary(u).map(Self::new)
-    }
-
-    #[inline]
-    fn size_hint(depth: usize) -> (usize, Option<usize>) {
-        <A as Arbitrary<'a>>::size_hint(depth)
-    }
-}
-
-impl<'a, A: Arbitrary<'a>> Arbitrary<'a> for UnsafeCell<A> {
-    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
-        Arbitrary::arbitrary(u).map(Self::new)
-    }
-
-    #[inline]
-    fn size_hint(depth: usize) -> (usize, Option<usize>) {
-        <A as Arbitrary<'a>>::size_hint(depth)
-    }
-}
-
-impl<'a, A: Arbitrary<'a>> Arbitrary<'a> for Mutex<A> {
-    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
-        Arbitrary::arbitrary(u).map(Self::new)
-    }
-
-    #[inline]
-    fn size_hint(depth: usize) -> (usize, Option<usize>) {
-        <A as Arbitrary<'a>>::size_hint(depth)
-    }
-}
-
-impl<'a, A: Arbitrary<'a>> Arbitrary<'a> for iter::Empty<A> {
-    fn arbitrary(_: &mut Unstructured<'a>) -> Result<Self> {
-        Ok(iter::empty())
-    }
-
-    #[inline]
-    fn size_hint(_depth: usize) -> (usize, Option<usize>) {
-        (0, Some(0))
-    }
-}
-
-impl<'a, A: Arbitrary<'a>> Arbitrary<'a> for ::std::marker::PhantomData<A> {
-    fn arbitrary(_: &mut Unstructured<'a>) -> Result<Self> {
-        Ok(::std::marker::PhantomData)
-    }
-
-    #[inline]
-    fn size_hint(_depth: usize) -> (usize, Option<usize>) {
-        (0, Some(0))
-    }
-}
-
-impl<'a, A: Arbitrary<'a>> Arbitrary<'a> for ::std::num::Wrapping<A> {
-    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
-        Arbitrary::arbitrary(u).map(::std::num::Wrapping)
-    }
-
-    #[inline]
-    fn size_hint(depth: usize) -> (usize, Option<usize>) {
-        <A as Arbitrary<'a>>::size_hint(depth)
-    }
-}
-
-macro_rules! implement_nonzero_int {
-    ($nonzero:ty, $int:ty) => {
-        impl<'a> Arbitrary<'a> for $nonzero {
-            fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
-                match Self::new(<$int as Arbitrary<'a>>::arbitrary(u)?) {
-                    Some(n) => Ok(n),
-                    None => Err(Error::IncorrectFormat),
-                }
-            }
-
-            #[inline]
-            fn size_hint(depth: usize) -> (usize, Option<usize>) {
-                <$int as Arbitrary<'a>>::size_hint(depth)
-            }
-        }
-    };
-}
-
-implement_nonzero_int! { NonZeroI8, i8 }
-implement_nonzero_int! { NonZeroI16, i16 }
-implement_nonzero_int! { NonZeroI32, i32 }
-implement_nonzero_int! { NonZeroI64, i64 }
-implement_nonzero_int! { NonZeroI128, i128 }
-implement_nonzero_int! { NonZeroIsize, isize }
-implement_nonzero_int! { NonZeroU8, u8 }
-implement_nonzero_int! { NonZeroU16, u16 }
-implement_nonzero_int! { NonZeroU32, u32 }
-implement_nonzero_int! { NonZeroU64, u64 }
-implement_nonzero_int! { NonZeroU128, u128 }
-implement_nonzero_int! { NonZeroUsize, usize }
-
-impl<'a> Arbitrary<'a> for Ipv4Addr {
-    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
-        Ok(Ipv4Addr::from(u32::arbitrary(u)?))
-    }
-
-    #[inline]
-    fn size_hint(_depth: usize) -> (usize, Option<usize>) {
-        (4, Some(4))
-    }
-}
-
-impl<'a> Arbitrary<'a> for Ipv6Addr {
-    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
-        Ok(Ipv6Addr::from(u128::arbitrary(u)?))
-    }
-
-    #[inline]
-    fn size_hint(_depth: usize) -> (usize, Option<usize>) {
-        (16, Some(16))
-    }
-}
-
-#[cfg(test)]
-mod test {
-    use super::*;
-
-    /// Generates an arbitrary `T`, and checks that the result is consistent with the
-    /// `size_hint()` reported by `T`.
-    fn checked_arbitrary<'a, T: Arbitrary<'a>>(u: &mut Unstructured<'a>) -> Result<T> {
-        let (min, max) = T::size_hint(0);
-
-        let len_before = u.len();
-        let result = T::arbitrary(u);
-
-        let consumed = len_before - u.len();
-
-        if let Some(max) = max {
-            assert!(
-                consumed <= max,
-                "incorrect maximum size: indicated {}, actually consumed {}",
-                max,
-                consumed
-            );
-        }
-
-        if result.is_ok() {
-            assert!(
-                consumed >= min,
-                "incorrect minimum size: indicated {}, actually consumed {}",
-                min,
-                consumed
-            );
-        }
-
-        result
-    }
-
-    /// Like `checked_arbitrary()`, but calls `arbitrary_take_rest()` instead of `arbitrary()`.
-    fn checked_arbitrary_take_rest<'a, T: Arbitrary<'a>>(u: Unstructured<'a>) -> Result<T> {
-        let (min, _) = T::size_hint(0);
-
-        let len_before = u.len();
-        let result = T::arbitrary_take_rest(u);
-
-        if result.is_ok() {
-            assert!(
-                len_before >= min,
-                "incorrect minimum size: indicated {}, worked with {}",
-                min,
-                len_before
-            );
-        }
-
-        result
-    }
-
-    #[test]
-    fn finite_buffer_fill_buffer() {
-        let x = [1, 2, 3, 4];
-        let mut rb = Unstructured::new(&x);
-        let mut z = [0; 2];
-        rb.fill_buffer(&mut z).unwrap();
-        assert_eq!(z, [1, 2]);
-        rb.fill_buffer(&mut z).unwrap();
-        assert_eq!(z, [3, 4]);
-        rb.fill_buffer(&mut z).unwrap();
-        assert_eq!(z, [0, 0]);
-    }
-
-    #[test]
-    fn arbitrary_for_integers() {
-        let x = [1, 2, 3, 4];
-        let mut buf = Unstructured::new(&x);
-        let expected = 1 | (2 << 8) | (3 << 16) | (4 << 24);
-        let actual = checked_arbitrary::<i32>(&mut buf).unwrap();
-        assert_eq!(expected, actual);
-    }
-
-    #[test]
-    fn arbitrary_for_bytes() {
-        let x = [1, 2, 3, 4, 4];
-        let mut buf = Unstructured::new(&x);
-        let expected = &[1, 2, 3, 4];
-        let actual = checked_arbitrary::<&[u8]>(&mut buf).unwrap();
-        assert_eq!(expected, actual);
-    }
-
-    #[test]
-    fn arbitrary_take_rest_for_bytes() {
-        let x = [1, 2, 3, 4];
-        let buf = Unstructured::new(&x);
-        let expected = &[1, 2, 3, 4];
-        let actual = checked_arbitrary_take_rest::<&[u8]>(buf).unwrap();
-        assert_eq!(expected, actual);
-    }
-
-    #[test]
-    fn arbitrary_collection() {
-        let x = [
-            1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9, 8, 12,
-        ];
-        assert_eq!(
-            checked_arbitrary::<&[u8]>(&mut Unstructured::new(&x)).unwrap(),
-            &[1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3]
-        );
-        assert_eq!(
-            checked_arbitrary::<Vec<u8>>(&mut Unstructured::new(&x)).unwrap(),
-            &[2, 4, 6, 8, 1]
-        );
-        assert_eq!(
-            checked_arbitrary::<Vec<u32>>(&mut Unstructured::new(&x)).unwrap(),
-            &[84148994]
-        );
-        assert_eq!(
-            checked_arbitrary::<String>(&mut Unstructured::new(&x)).unwrap(),
-            "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x01\x02\x03"
-        );
-    }
-
-    #[test]
-    fn arbitrary_take_rest() {
-        let x = [1, 2, 3, 4];
-        assert_eq!(
-            checked_arbitrary_take_rest::<&[u8]>(Unstructured::new(&x)).unwrap(),
-            &[1, 2, 3, 4]
-        );
-        assert_eq!(
-            checked_arbitrary_take_rest::<Vec<u8>>(Unstructured::new(&x)).unwrap(),
-            &[1, 2, 3, 4]
-        );
-        assert_eq!(
-            checked_arbitrary_take_rest::<Vec<u32>>(Unstructured::new(&x)).unwrap(),
-            &[0x4030201]
-        );
-        assert_eq!(
-            checked_arbitrary_take_rest::<String>(Unstructured::new(&x)).unwrap(),
-            "\x01\x02\x03\x04"
-        );
-
-        assert_eq!(
-            checked_arbitrary_take_rest::<&[u8]>(Unstructured::new(&[])).unwrap(),
-            &[]
-        );
-        assert_eq!(
-            checked_arbitrary_take_rest::<Vec<u8>>(Unstructured::new(&[])).unwrap(),
-            &[]
-        );
-    }
-
-    #[test]
-    fn size_hint_for_tuples() {
-        assert_eq!(
-            (7, Some(7)),
-            <(bool, u16, i32) as Arbitrary<'_>>::size_hint(0)
-        );
-        assert_eq!((1, None), <(u8, Vec<u8>) as Arbitrary>::size_hint(0));
+    fn try_size_hint(depth: usize) -> Result<(usize, Option<usize>), MaxRecursionReached> {
+        Ok(Self::size_hint(depth))
     }
 }
 
@@ -1338,5 +470,65 @@
 ///     x: i32,
 /// }
 /// ```
+///
+/// Multiple conflicting bounds at the container-level:
+/// ```compile_fail
+/// #[derive(::arbitrary::Arbitrary)]
+/// #[arbitrary(bound = "T: Default")]
+/// #[arbitrary(bound = "T: Default")]
+/// struct Point<T: Default> {
+///     #[arbitrary(default)]
+///     x: T,
+/// }
+/// ```
+///
+/// Multiple conflicting bounds in a single bound attribute:
+/// ```compile_fail
+/// #[derive(::arbitrary::Arbitrary)]
+/// #[arbitrary(bound = "T: Default, T: Default")]
+/// struct Point<T: Default> {
+///     #[arbitrary(default)]
+///     x: T,
+/// }
+/// ```
+///
+/// Multiple conflicting bounds in multiple bound attributes:
+/// ```compile_fail
+/// #[derive(::arbitrary::Arbitrary)]
+/// #[arbitrary(bound = "T: Default", bound = "T: Default")]
+/// struct Point<T: Default> {
+///     #[arbitrary(default)]
+///     x: T,
+/// }
+/// ```
+///
+/// Too many bounds supplied:
+/// ```compile_fail
+/// #[derive(::arbitrary::Arbitrary)]
+/// #[arbitrary(bound = "T: Default")]
+/// struct Point {
+///     x: i32,
+/// }
+/// ```
+///
+/// Too many bounds supplied across multiple attributes:
+/// ```compile_fail
+/// #[derive(::arbitrary::Arbitrary)]
+/// #[arbitrary(bound = "T: Default")]
+/// #[arbitrary(bound = "U: Default")]
+/// struct Point<T: Default> {
+///     #[arbitrary(default)]
+///     x: T,
+/// }
+/// ```
+///
+/// Attempt to use the derive attribute on an enum variant:
+/// ```compile_fail
+/// #[derive(::arbitrary::Arbitrary)]
+/// enum Enum<T: Default> {
+///     #[arbitrary(default)]
+///     Variant(T),
+/// }
+/// ```
 #[cfg(all(doctest, feature = "derive"))]
 pub struct CompileFailTests;
diff --git a/crates/arbitrary/src/size_hint.rs b/crates/arbitrary/src/size_hint.rs
index 045c148..95707ee 100644
--- a/crates/arbitrary/src/size_hint.rs
+++ b/crates/arbitrary/src/size_hint.rs
@@ -1,6 +1,8 @@
 //! Utilities for working with and combining the results of
 //! [`Arbitrary::size_hint`][crate::Arbitrary::size_hint].
 
+pub(crate) const MAX_DEPTH: usize = 20;
+
 /// Protects against potential infinite recursion when calculating size hints
 /// due to indirect type recursion.
 ///
@@ -8,12 +10,14 @@
 /// size hint.
 ///
 /// Otherwise, returns the default size hint: `(0, None)`.
+///
+/// <div class="warning">This method is deprecated. Users should instead implement <a href="../trait.Arbitrary.html#method.try_size_hint"><code>try_size_hint</code></a> and use <a href="fn.try_recursion_guard.html"><code>try_recursion_guard</code></a></div>
 #[inline]
+#[deprecated(note = "use `try_recursion_guard` instead")]
 pub fn recursion_guard(
     depth: usize,
     f: impl FnOnce(usize) -> (usize, Option<usize>),
 ) -> (usize, Option<usize>) {
-    const MAX_DEPTH: usize = 20;
     if depth > MAX_DEPTH {
         (0, None)
     } else {
@@ -21,6 +25,27 @@
     }
 }
 
+/// Protects against potential infinite recursion when calculating size hints
+/// due to indirect type recursion.
+///
+/// When the depth is not too deep, calls `f` with `depth + 1` to calculate the
+/// size hint.
+///
+/// Otherwise, returns an error.
+///
+/// This should be used when implementing [`try_size_hint`](crate::Arbitrary::try_size_hint)
+#[inline]
+pub fn try_recursion_guard(
+    depth: usize,
+    f: impl FnOnce(usize) -> Result<(usize, Option<usize>), crate::MaxRecursionReached>,
+) -> Result<(usize, Option<usize>), crate::MaxRecursionReached> {
+    if depth > MAX_DEPTH {
+        Err(crate::MaxRecursionReached {})
+    } else {
+        f(depth + 1)
+    }
+}
+
 /// Take the sum of the `lhs` and `rhs` size hints.
 #[inline]
 pub fn and(lhs: (usize, Option<usize>), rhs: (usize, Option<usize>)) -> (usize, Option<usize>) {
diff --git a/crates/arbitrary/src/tests.rs b/crates/arbitrary/src/tests.rs
new file mode 100644
index 0000000..7746715
--- /dev/null
+++ b/crates/arbitrary/src/tests.rs
@@ -0,0 +1,316 @@
+use {
+    super::{Arbitrary, Result, Unstructured},
+    std::{collections::HashSet, fmt::Debug, hash::Hash, rc::Rc, sync::Arc},
+};
+
+/// Assert that the given expected values are all generated.
+///
+/// Exhaustively enumerates all buffers up to length 10 containing the
+/// following bytes: `0x00`, `0x01`, `0x61` (aka ASCII 'a'), and `0xff`
+fn assert_generates<T>(expected_values: impl IntoIterator<Item = T>)
+where
+    T: Clone + Debug + Hash + Eq + for<'a> Arbitrary<'a>,
+{
+    let expected_values: HashSet<_> = expected_values.into_iter().collect();
+    let mut arbitrary_expected = expected_values.clone();
+    let mut arbitrary_take_rest_expected = expected_values;
+
+    let bytes = [0, 1, b'a', 0xff];
+    let max_len = 10;
+
+    let mut buf = Vec::with_capacity(max_len);
+
+    let mut g = exhaustigen::Gen::new();
+    while !g.done() {
+        let len = g.gen(max_len);
+
+        buf.clear();
+        buf.extend(
+            std::iter::repeat_with(|| {
+                let index = g.gen(bytes.len() - 1);
+                bytes[index]
+            })
+            .take(len),
+        );
+
+        let mut u = Unstructured::new(&buf);
+        let val = T::arbitrary(&mut u).unwrap();
+        arbitrary_expected.remove(&val);
+
+        let u = Unstructured::new(&buf);
+        let val = T::arbitrary_take_rest(u).unwrap();
+        arbitrary_take_rest_expected.remove(&val);
+
+        if arbitrary_expected.is_empty() && arbitrary_take_rest_expected.is_empty() {
+            return;
+        }
+    }
+
+    panic!(
+        "failed to generate all expected values!\n\n\
+         T::arbitrary did not generate: {arbitrary_expected:#?}\n\n\
+         T::arbitrary_take_rest did not generate {arbitrary_take_rest_expected:#?}"
+    )
+}
+
+/// Generates an arbitrary `T`, and checks that the result is consistent with the
+/// `size_hint()` reported by `T`.
+fn checked_arbitrary<'a, T: Arbitrary<'a>>(u: &mut Unstructured<'a>) -> Result<T> {
+    let (min, max) = T::size_hint(0);
+
+    let len_before = u.len();
+    let result = T::arbitrary(u);
+
+    let consumed = len_before - u.len();
+
+    if let Some(max) = max {
+        assert!(
+            consumed <= max,
+            "incorrect maximum size: indicated {}, actually consumed {}",
+            max,
+            consumed
+        );
+    }
+
+    if result.is_ok() {
+        assert!(
+            consumed >= min,
+            "incorrect minimum size: indicated {}, actually consumed {}",
+            min,
+            consumed
+        );
+    }
+
+    result
+}
+
+/// Like `checked_arbitrary()`, but calls `arbitrary_take_rest()` instead of `arbitrary()`.
+fn checked_arbitrary_take_rest<'a, T: Arbitrary<'a>>(u: Unstructured<'a>) -> Result<T> {
+    let (min, _) = T::size_hint(0);
+
+    let len_before = u.len();
+    let result = T::arbitrary_take_rest(u);
+
+    if result.is_ok() {
+        assert!(
+            len_before >= min,
+            "incorrect minimum size: indicated {}, worked with {}",
+            min,
+            len_before
+        );
+    }
+
+    result
+}
+
+#[test]
+fn finite_buffer_fill_buffer() {
+    let x = [1, 2, 3, 4];
+    let mut rb = Unstructured::new(&x);
+    let mut z = [0; 2];
+    rb.fill_buffer(&mut z).unwrap();
+    assert_eq!(z, [1, 2]);
+    rb.fill_buffer(&mut z).unwrap();
+    assert_eq!(z, [3, 4]);
+    rb.fill_buffer(&mut z).unwrap();
+    assert_eq!(z, [0, 0]);
+}
+
+#[test]
+fn arbitrary_for_integers() {
+    let x = [1, 2, 3, 4];
+    let mut buf = Unstructured::new(&x);
+    let expected = 1 | (2 << 8) | (3 << 16) | (4 << 24);
+    let actual = checked_arbitrary::<i32>(&mut buf).unwrap();
+    assert_eq!(expected, actual);
+
+    assert_generates([
+        i32::from_ne_bytes([0, 0, 0, 0]),
+        i32::from_ne_bytes([0, 0, 0, 1]),
+        i32::from_ne_bytes([0, 0, 1, 0]),
+        i32::from_ne_bytes([0, 1, 0, 0]),
+        i32::from_ne_bytes([1, 0, 0, 0]),
+        i32::from_ne_bytes([1, 1, 1, 1]),
+        i32::from_ne_bytes([0xff, 0xff, 0xff, 0xff]),
+    ]);
+}
+
+#[test]
+fn arbitrary_for_bytes() {
+    let x = [1, 2, 3, 4, 4];
+    let mut buf = Unstructured::new(&x);
+    let expected = &[1, 2, 3, 4];
+    let actual = checked_arbitrary::<&[u8]>(&mut buf).unwrap();
+    assert_eq!(expected, actual);
+}
+
+#[test]
+fn arbitrary_take_rest_for_bytes() {
+    let x = [1, 2, 3, 4];
+    let buf = Unstructured::new(&x);
+    let expected = &[1, 2, 3, 4];
+    let actual = checked_arbitrary_take_rest::<&[u8]>(buf).unwrap();
+    assert_eq!(expected, actual);
+}
+
+#[test]
+fn arbitrary_for_vec_u8() {
+    assert_generates::<Vec<u8>>([
+        vec![],
+        vec![0],
+        vec![1],
+        vec![0, 0],
+        vec![0, 1],
+        vec![1, 0],
+        vec![1, 1],
+        vec![0, 0, 0],
+        vec![0, 0, 1],
+        vec![0, 1, 0],
+        vec![0, 1, 1],
+        vec![1, 0, 0],
+        vec![1, 0, 1],
+        vec![1, 1, 0],
+        vec![1, 1, 1],
+    ]);
+}
+
+#[test]
+fn arbitrary_for_vec_vec_u8() {
+    assert_generates::<Vec<Vec<u8>>>([
+        vec![],
+        vec![vec![]],
+        vec![vec![0]],
+        vec![vec![1]],
+        vec![vec![0, 1]],
+        vec![vec![], vec![]],
+        vec![vec![0], vec![]],
+        vec![vec![], vec![1]],
+        vec![vec![0], vec![1]],
+        vec![vec![0, 1], vec![]],
+        vec![vec![], vec![1, 0]],
+        vec![vec![], vec![], vec![]],
+    ]);
+}
+
+#[test]
+fn arbitrary_for_vec_vec_vec_u8() {
+    assert_generates::<Vec<Vec<Vec<u8>>>>([
+        vec![],
+        vec![vec![]],
+        vec![vec![vec![0]]],
+        vec![vec![vec![1]]],
+        vec![vec![vec![0, 1]]],
+        vec![vec![], vec![]],
+        vec![vec![], vec![vec![]]],
+        vec![vec![vec![]], vec![]],
+        vec![vec![vec![]], vec![vec![]]],
+        vec![vec![vec![0]], vec![]],
+        vec![vec![], vec![vec![1]]],
+        vec![vec![vec![0]], vec![vec![1]]],
+        vec![vec![vec![0, 1]], vec![]],
+        vec![vec![], vec![vec![0, 1]]],
+        vec![vec![], vec![], vec![]],
+        vec![vec![vec![]], vec![], vec![]],
+        vec![vec![], vec![vec![]], vec![]],
+        vec![vec![], vec![], vec![vec![]]],
+    ]);
+}
+
+#[test]
+fn arbitrary_for_string() {
+    assert_generates::<String>(["".into(), "a".into(), "aa".into(), "aaa".into()]);
+}
+
+#[test]
+fn arbitrary_collection() {
+    let x = [
+        1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9, 8, 12,
+    ];
+    assert_eq!(
+        checked_arbitrary::<&[u8]>(&mut Unstructured::new(&x)).unwrap(),
+        &[1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3]
+    );
+    assert_eq!(
+        checked_arbitrary::<Vec<u8>>(&mut Unstructured::new(&x)).unwrap(),
+        &[2, 4, 6, 8, 1]
+    );
+    assert_eq!(
+        &*checked_arbitrary::<Box<[u8]>>(&mut Unstructured::new(&x)).unwrap(),
+        &[2, 4, 6, 8, 1]
+    );
+    assert_eq!(
+        &*checked_arbitrary::<Arc<[u8]>>(&mut Unstructured::new(&x)).unwrap(),
+        &[2, 4, 6, 8, 1]
+    );
+    assert_eq!(
+        &*checked_arbitrary::<Rc<[u8]>>(&mut Unstructured::new(&x)).unwrap(),
+        &[2, 4, 6, 8, 1]
+    );
+    assert_eq!(
+        checked_arbitrary::<Vec<u32>>(&mut Unstructured::new(&x)).unwrap(),
+        &[84148994]
+    );
+    assert_eq!(
+        checked_arbitrary::<String>(&mut Unstructured::new(&x)).unwrap(),
+        "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x01\x02\x03"
+    );
+}
+
+#[test]
+fn arbitrary_take_rest() {
+    // Basic examples
+    let x = [1, 2, 3, 4];
+    assert_eq!(
+        checked_arbitrary_take_rest::<&[u8]>(Unstructured::new(&x)).unwrap(),
+        &[1, 2, 3, 4]
+    );
+    assert_eq!(
+        checked_arbitrary_take_rest::<Vec<u8>>(Unstructured::new(&x)).unwrap(),
+        &[2, 4]
+    );
+    assert_eq!(
+        &*checked_arbitrary_take_rest::<Box<[u8]>>(Unstructured::new(&x)).unwrap(),
+        &[2, 4]
+    );
+    assert_eq!(
+        &*checked_arbitrary_take_rest::<Arc<[u8]>>(Unstructured::new(&x)).unwrap(),
+        &[2, 4]
+    );
+    assert_eq!(
+        &*checked_arbitrary_take_rest::<Rc<[u8]>>(Unstructured::new(&x)).unwrap(),
+        &[2, 4]
+    );
+    assert_eq!(
+        checked_arbitrary_take_rest::<Vec<u32>>(Unstructured::new(&x)).unwrap(),
+        &[0x040302]
+    );
+    assert_eq!(
+        checked_arbitrary_take_rest::<String>(Unstructured::new(&x)).unwrap(),
+        "\x01\x02\x03\x04"
+    );
+
+    // Empty remainder
+    assert_eq!(
+        checked_arbitrary_take_rest::<&[u8]>(Unstructured::new(&[])).unwrap(),
+        &[]
+    );
+    assert_eq!(
+        checked_arbitrary_take_rest::<Vec<u8>>(Unstructured::new(&[])).unwrap(),
+        &[]
+    );
+
+    // Cannot consume all but can consume part of the input
+    assert_eq!(
+        checked_arbitrary_take_rest::<String>(Unstructured::new(&[1, 0xFF, 2])).unwrap(),
+        "\x01"
+    );
+}
+
+#[test]
+fn size_hint_for_tuples() {
+    assert_eq!(
+        (7, Some(7)),
+        <(bool, u16, i32) as Arbitrary<'_>>::size_hint(0)
+    );
+    assert_eq!((1, None), <(u8, Vec<u8>) as Arbitrary>::size_hint(0));
+}
diff --git a/crates/arbitrary/src/unstructured.rs b/crates/arbitrary/src/unstructured.rs
index 0bfdff2..8b7da26 100644
--- a/crates/arbitrary/src/unstructured.rs
+++ b/crates/arbitrary/src/unstructured.rs
@@ -68,6 +68,7 @@
 /// }
 /// # }
 /// ```
+#[derive(Debug)]
 pub struct Unstructured<'a> {
     data: &'a [u8],
 }
@@ -236,20 +237,20 @@
 
             // We only consume as many bytes as necessary to cover the entire
             // range of the byte string.
-            // Note: We cast to u64 so we don't overflow when checking std::u32::MAX + 4 on 32-bit archs
-            let len = if self.data.len() as u64 <= std::u8::MAX as u64 + 1 {
+            // Note: We cast to u64 so we don't overflow when checking u32::MAX + 4 on 32-bit archs
+            let len = if self.data.len() as u64 <= u8::MAX as u64 + 1 {
                 let bytes = 1;
                 let max_size = self.data.len() - bytes;
                 let (rest, for_size) = self.data.split_at(max_size);
                 self.data = rest;
                 Self::int_in_range_impl(0..=max_size as u8, for_size.iter().copied())?.0 as usize
-            } else if self.data.len() as u64 <= std::u16::MAX as u64 + 2 {
+            } else if self.data.len() as u64 <= u16::MAX as u64 + 2 {
                 let bytes = 2;
                 let max_size = self.data.len() - bytes;
                 let (rest, for_size) = self.data.split_at(max_size);
                 self.data = rest;
                 Self::int_in_range_impl(0..=max_size as u16, for_size.iter().copied())?.0 as usize
-            } else if self.data.len() as u64 <= std::u32::MAX as u64 + 4 {
+            } else if self.data.len() as u64 <= u32::MAX as u64 + 4 {
                 let bytes = 4;
                 let max_size = self.data.len() - bytes;
                 let (rest, for_size) = self.data.split_at(max_size);
@@ -280,15 +281,16 @@
     /// # Example
     ///
     /// ```
+    /// # fn foo() -> arbitrary::Result<()> {
     /// use arbitrary::{Arbitrary, Unstructured};
     ///
     /// let mut u = Unstructured::new(&[1, 2, 3, 4]);
     ///
-    /// let x: i32 = u.int_in_range(-5_000..=-1_000)
-    ///     .expect("constructed `u` with enough bytes to generate an `i32`");
+    /// let x: i32 = u.int_in_range(-5_000..=-1_000)?;
     ///
     /// assert!(-5_000 <= x);
     /// assert!(x <= -1_000);
+    /// # Ok(()) }
     /// ```
     pub fn int_in_range<T>(&mut self, range: ops::RangeInclusive<T>) -> Result<T>
     where
@@ -410,6 +412,41 @@
         Ok(&choices[idx])
     }
 
+    /// Choose one of the given iterator choices.
+    ///
+    /// This should only be used inside of `Arbitrary` implementations.
+    ///
+    /// Returns an error if there is not enough underlying data to make a
+    /// choice or if no choices are provided.
+    ///
+    /// # Examples
+    ///
+    /// Selecting a random item from a set:
+    ///
+    /// ```
+    /// use std::collections::BTreeSet;
+    /// use arbitrary::Unstructured;
+    ///
+    /// let mut u = Unstructured::new(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]);
+    /// let set = BTreeSet::from(['a', 'b', 'c']);
+    ///
+    /// let choice = u.choose_iter(set.iter()).unwrap();
+    ///
+    /// println!("chose {}", choice);
+    /// ```
+    pub fn choose_iter<T, I>(&mut self, choices: I) -> Result<T>
+    where
+        I: IntoIterator<Item = T>,
+        I::IntoIter: ExactSizeIterator,
+    {
+        let mut choices = choices.into_iter();
+        let idx = self.choose_index(choices.len())?;
+        let choice = choices
+            .nth(idx)
+            .expect("ExactSizeIterator should have correct len");
+        Ok(choice)
+    }
+
     /// Choose a value in `0..len`.
     ///
     /// Returns an error if the `len` is zero.
@@ -620,14 +657,8 @@
     pub fn arbitrary_take_rest_iter<ElementType: Arbitrary<'a>>(
         self,
     ) -> Result<ArbitraryTakeRestIter<'a, ElementType>> {
-        let (lower, upper) = ElementType::size_hint(0);
-
-        let elem_size = upper.unwrap_or(lower * 2);
-        let elem_size = std::cmp::max(1, elem_size);
-        let size = self.len() / elem_size;
         Ok(ArbitraryTakeRestIter {
-            size,
-            u: Some(self),
+            u: self,
             _marker: PhantomData,
         })
     }
@@ -735,25 +766,16 @@
 
 /// Utility iterator produced by [`Unstructured::arbitrary_take_rest_iter`]
 pub struct ArbitraryTakeRestIter<'a, ElementType> {
-    u: Option<Unstructured<'a>>,
-    size: usize,
+    u: Unstructured<'a>,
     _marker: PhantomData<ElementType>,
 }
 
 impl<'a, ElementType: Arbitrary<'a>> Iterator for ArbitraryTakeRestIter<'a, ElementType> {
     type Item = Result<ElementType>;
     fn next(&mut self) -> Option<Result<ElementType>> {
-        if let Some(mut u) = self.u.take() {
-            if self.size == 1 {
-                Some(Arbitrary::arbitrary_take_rest(u))
-            } else if self.size == 0 {
-                None
-            } else {
-                self.size -= 1;
-                let ret = Arbitrary::arbitrary(&mut u);
-                self.u = Some(u);
-                Some(ret)
-            }
+        let keep_going = self.u.arbitrary().unwrap_or(false);
+        if keep_going {
+            Some(Arbitrary::arbitrary(&mut self.u))
         } else {
             None
         }
@@ -890,8 +912,7 @@
         // Should take one byte off the end
         assert_eq!(u.arbitrary_byte_size().unwrap(), 6);
         assert_eq!(u.len(), 9);
-        let mut v = vec![];
-        v.resize(260, 0);
+        let mut v = vec![0; 260];
         v.push(1);
         v.push(4);
         let mut u = Unstructured::new(&v);
diff --git a/crates/arbitrary/tests/bound.rs b/crates/arbitrary/tests/bound.rs
new file mode 100644
index 0000000..7a772ac
--- /dev/null
+++ b/crates/arbitrary/tests/bound.rs
@@ -0,0 +1,142 @@
+#![cfg(feature = "derive")]
+
+use arbitrary::{Arbitrary, Unstructured};
+
+fn arbitrary_from<'a, T: Arbitrary<'a>>(input: &'a [u8]) -> T {
+    let mut buf = Unstructured::new(input);
+    T::arbitrary(&mut buf).expect("can create arbitrary instance OK")
+}
+
+/// This wrapper trait *implies* `Arbitrary`, but the compiler isn't smart enough to work that out
+/// so when using this wrapper we *must* opt-out of the auto-generated `T: Arbitrary` bounds.
+pub trait WrapperTrait: for<'a> Arbitrary<'a> {}
+
+impl WrapperTrait for u32 {}
+
+#[derive(Arbitrary)]
+#[arbitrary(bound = "T: WrapperTrait")]
+struct GenericSingleBound<T: WrapperTrait> {
+    t: T,
+}
+
+#[test]
+fn single_bound() {
+    let v: GenericSingleBound<u32> = arbitrary_from(&[0, 0, 0, 0]);
+    assert_eq!(v.t, 0);
+}
+
+#[derive(Arbitrary)]
+#[arbitrary(bound = "T: WrapperTrait, U: WrapperTrait")]
+struct GenericMultipleBoundsSingleAttribute<T: WrapperTrait, U: WrapperTrait> {
+    t: T,
+    u: U,
+}
+
+#[test]
+fn multiple_bounds_single_attribute() {
+    let v: GenericMultipleBoundsSingleAttribute<u32, u32> =
+        arbitrary_from(&[1, 0, 0, 0, 2, 0, 0, 0]);
+    assert_eq!(v.t, 1);
+    assert_eq!(v.u, 2);
+}
+
+#[derive(Arbitrary)]
+#[arbitrary(bound = "T: WrapperTrait")]
+#[arbitrary(bound = "U: Default")]
+struct GenericMultipleArbitraryAttributes<T: WrapperTrait, U: Default> {
+    t: T,
+    #[arbitrary(default)]
+    u: U,
+}
+
+#[test]
+fn multiple_arbitrary_attributes() {
+    let v: GenericMultipleArbitraryAttributes<u32, u32> = arbitrary_from(&[1, 0, 0, 0]);
+    assert_eq!(v.t, 1);
+    assert_eq!(v.u, 0);
+}
+
+#[derive(Arbitrary)]
+#[arbitrary(bound = "T: WrapperTrait", bound = "U: Default")]
+struct GenericMultipleBoundAttributes<T: WrapperTrait, U: Default> {
+    t: T,
+    #[arbitrary(default)]
+    u: U,
+}
+
+#[test]
+fn multiple_bound_attributes() {
+    let v: GenericMultipleBoundAttributes<u32, u32> = arbitrary_from(&[1, 0, 0, 0]);
+    assert_eq!(v.t, 1);
+    assert_eq!(v.u, 0);
+}
+
+#[derive(Arbitrary)]
+#[arbitrary(bound = "T: WrapperTrait", bound = "U: Default")]
+#[arbitrary(bound = "V: WrapperTrait, W: Default")]
+struct GenericMultipleArbitraryAndBoundAttributes<
+    T: WrapperTrait,
+    U: Default,
+    V: WrapperTrait,
+    W: Default,
+> {
+    t: T,
+    #[arbitrary(default)]
+    u: U,
+    v: V,
+    #[arbitrary(default)]
+    w: W,
+}
+
+#[test]
+fn multiple_arbitrary_and_bound_attributes() {
+    let v: GenericMultipleArbitraryAndBoundAttributes<u32, u32, u32, u32> =
+        arbitrary_from(&[1, 0, 0, 0, 2, 0, 0, 0]);
+    assert_eq!(v.t, 1);
+    assert_eq!(v.u, 0);
+    assert_eq!(v.v, 2);
+    assert_eq!(v.w, 0);
+}
+
+#[derive(Arbitrary)]
+#[arbitrary(bound = "T: Default")]
+struct GenericDefault<T: Default> {
+    #[arbitrary(default)]
+    x: T,
+}
+
+#[test]
+fn default_bound() {
+    // We can write a generic func without any `Arbitrary` bound.
+    fn generic_default<T: Default>() -> GenericDefault<T> {
+        arbitrary_from(&[])
+    }
+
+    assert_eq!(generic_default::<u64>().x, 0);
+    assert_eq!(generic_default::<String>().x, String::new());
+    assert_eq!(generic_default::<Vec<u8>>().x, Vec::new());
+}
+
+#[derive(Arbitrary)]
+#[arbitrary()]
+struct EmptyArbitraryAttribute {
+    t: u32,
+}
+
+#[test]
+fn empty_arbitrary_attribute() {
+    let v: EmptyArbitraryAttribute = arbitrary_from(&[1, 0, 0, 0]);
+    assert_eq!(v.t, 1);
+}
+
+#[derive(Arbitrary)]
+#[arbitrary(bound = "")]
+struct EmptyBoundAttribute {
+    t: u32,
+}
+
+#[test]
+fn empty_bound_attribute() {
+    let v: EmptyBoundAttribute = arbitrary_from(&[1, 0, 0, 0]);
+    assert_eq!(v.t, 1);
+}
diff --git a/crates/arbitrary/tests/derive.rs b/crates/arbitrary/tests/derive.rs
index f29d227..880d146 100644
--- a/crates/arbitrary/tests/derive.rs
+++ b/crates/arbitrary/tests/derive.rs
@@ -2,6 +2,9 @@
 // Various structs/fields that we are deriving `Arbitrary` for aren't actually
 // used except to exercise the derive.
 #![allow(dead_code)]
+// Various assert_eq! are used to compare result of bool amongst other data types
+// In this case, using assert! is less explicit and readable
+#![allow(clippy::bool_assert_comparison)]
 
 use arbitrary::*;
 
@@ -59,7 +62,7 @@
     assert_eq!(s2.1, true);
     assert_eq!(s1.2, 0x4030201);
     assert_eq!(s2.2, 0x4030201);
-    assert_eq!(s1.3, vec![0x605, 0x807]);
+    assert_eq!(s1.3, vec![0x0706]);
     assert_eq!(s2.3, "\x05\x06\x07\x08");
 }
 
@@ -116,6 +119,30 @@
     assert_eq!((4, Some(17)), <MyEnum as Arbitrary>::size_hint(0));
 }
 
+// This should result in a compiler-error:
+// #[derive(Arbitrary, Debug)]
+// enum Never {
+//     #[arbitrary(skip)]
+//     Nope,
+// }
+
+#[derive(Arbitrary, Debug)]
+enum SkipVariant {
+    Always,
+    #[arbitrary(skip)]
+    Never,
+}
+
+#[test]
+fn test_skip_variant() {
+    (0..=u8::MAX).for_each(|byte| {
+        let buffer = [byte];
+        let unstructured = Unstructured::new(&buffer);
+        let skip_variant = SkipVariant::arbitrary_take_rest(unstructured).unwrap();
+        assert!(!matches!(skip_variant, SkipVariant::Never));
+    })
+}
+
 #[derive(Arbitrary, Debug)]
 enum RecursiveTree {
     Leaf,
@@ -125,13 +152,86 @@
     },
 }
 
+#[derive(Arbitrary, Debug)]
+struct WideRecursiveStruct {
+    a: Option<Box<WideRecursiveStruct>>,
+    b: Option<Box<WideRecursiveStruct>>,
+    c: Option<Box<WideRecursiveStruct>>,
+    d: Option<Box<WideRecursiveStruct>>,
+    e: Option<Box<WideRecursiveStruct>>,
+    f: Option<Box<WideRecursiveStruct>>,
+    g: Option<Box<WideRecursiveStruct>>,
+    h: Option<Box<WideRecursiveStruct>>,
+    i: Option<Box<WideRecursiveStruct>>,
+    k: Option<Box<WideRecursiveStruct>>,
+}
+
+#[derive(Arbitrary, Debug)]
+enum WideRecursiveEnum {
+    None,
+    A(Box<WideRecursiveStruct>),
+    B(Box<WideRecursiveStruct>),
+    C(Box<WideRecursiveStruct>),
+    D(Box<WideRecursiveStruct>),
+    E(Box<WideRecursiveStruct>),
+    F(Box<WideRecursiveStruct>),
+    G(Box<WideRecursiveStruct>),
+    H(Box<WideRecursiveStruct>),
+    I(Box<WideRecursiveStruct>),
+    K(Box<WideRecursiveStruct>),
+}
+
+#[derive(Arbitrary, Debug)]
+enum WideRecursiveMixedEnum {
+    None,
+    A(Box<WideRecursiveMixedEnum>),
+    B(Box<WideRecursiveMixedEnum>),
+    C(Box<WideRecursiveMixedEnum>),
+    D(Box<WideRecursiveMixedEnum>),
+    E(Box<WideRecursiveMixedEnum>),
+    F(Box<WideRecursiveMixedStruct>),
+    G(Box<WideRecursiveMixedStruct>),
+    H(Box<WideRecursiveMixedStruct>),
+    I(Box<WideRecursiveMixedStruct>),
+    K(Box<WideRecursiveMixedStruct>),
+}
+
+#[derive(Arbitrary, Debug)]
+struct WideRecursiveMixedStruct {
+    a: Option<Box<WideRecursiveMixedEnum>>,
+    b: Option<Box<WideRecursiveMixedEnum>>,
+    c: Option<Box<WideRecursiveMixedEnum>>,
+    d: Option<Box<WideRecursiveMixedEnum>>,
+    e: Option<Box<WideRecursiveMixedEnum>>,
+    f: Option<Box<WideRecursiveMixedStruct>>,
+    g: Option<Box<WideRecursiveMixedStruct>>,
+    h: Option<Box<WideRecursiveMixedStruct>>,
+    i: Option<Box<WideRecursiveMixedStruct>>,
+    k: Option<Box<WideRecursiveMixedStruct>>,
+}
+
 #[test]
 fn recursive() {
     let raw = vec![1, 2, 3, 4, 5, 6, 7, 8, 9];
     let _rec: RecursiveTree = arbitrary_from(&raw);
+    let _rec: WideRecursiveStruct = arbitrary_from(&raw);
+    let _rec: WideRecursiveEnum = arbitrary_from(&raw);
+    let _rec: WideRecursiveMixedStruct = arbitrary_from(&raw);
+    let _rec: WideRecursiveMixedEnum = arbitrary_from(&raw);
+
+    assert_eq!((0, None), <WideRecursiveStruct as Arbitrary>::size_hint(0));
+    assert_eq!((0, None), <WideRecursiveEnum as Arbitrary>::size_hint(0));
+    assert_eq!(
+        (0, None),
+        <WideRecursiveMixedStruct as Arbitrary>::size_hint(0)
+    );
+    assert_eq!(
+        (0, None),
+        <WideRecursiveMixedEnum as Arbitrary>::size_hint(0)
+    );
 
     let (lower, upper) = <RecursiveTree as Arbitrary>::size_hint(0);
-    assert_eq!(lower, 4, "need a u32 for the discriminant at minimum");
+    assert_eq!(lower, 0, "Cannot compute size hint of recursive structure");
     assert!(
         upper.is_none(),
         "potentially infinitely recursive, so no upper bound"
@@ -276,3 +376,20 @@
     // 17 is the 3rd byte used by arbitrary
     assert_eq!(parcel.price, 17);
 }
+
+#[test]
+fn derive_structs_named_same_as_core() {
+    #[derive(Debug, Arbitrary)]
+    struct Option {
+        f: core::option::Option<u32>,
+    }
+
+    let _ = Option::arbitrary(&mut Unstructured::new(&[]));
+
+    #[derive(Debug, Default, Arbitrary)]
+    struct Default {
+        f: u32,
+    }
+
+    let _ = Default::arbitrary(&mut Unstructured::new(&[]));
+}
diff --git a/crates/derive_arbitrary/.android-checksum.json b/crates/derive_arbitrary/.android-checksum.json
index d0cd919..3c8eca9 100644
--- a/crates/derive_arbitrary/.android-checksum.json
+++ b/crates/derive_arbitrary/.android-checksum.json
@@ -1 +1 @@
-{"package":null,"files":{"METADATA":"c74f9be53fa880bfaaa35e791e79d8f0a0e89a8a4a93b1340f19819cb149fa67","MODULE_LICENSE_APACHE2":"0d6f8afa3940b7f06bebee651376d43bc8b0d5b437337be2696d30377451e93a","README.md":"94b361e8062eac8c5297a235595146e0758c2fe96a573776ae6022bcaf67508a","Android.bp":"ed61c2a0bf67f4a7c8b7bfba3558c9b875a46485140d15929e8b577a50a5583b","LICENSE-MIT":"3b270c5247fc2265dac124e9ada8609aa00c03289761bafa13c741aee60a504a","cargo_embargo.json":"aa45a963da01d3f018be316cd5b7646a5b413ce2611c5218f2914d2e8a9efd0e",".cargo-checksum.json":"ea186c95063a5399571e9174af2f4b96047e0d6b93e2514894d1a252d3a512dd","LICENSE-APACHE":"3c7cd2396b5b772507febd2615d3d5a55b80103845037df77c87ba6e64872f2c","src/lib.rs":"bf338d09989ad2e416d4cc8d6155e9c331f9f09a49eccbbc56e9925ce29465b5","src/field_attributes.rs":"cdb0ba9cd0b22fa3b6e2df50f86e777347940a731ac460312e64cd68ba11d13c","Cargo.toml":"67eb474e4cc4913e74be742c354d1764ee8c076d6e7132dfa0af8cffb1bb4ef1","src/container_attributes.rs":"b3132133bb66fa6f34ca83d6f2c4e8e8c85b7719ddf41b33ab4724e5af38b722","LICENSE":"3c7cd2396b5b772507febd2615d3d5a55b80103845037df77c87ba6e64872f2c","TEST_MAPPING":"02e3608ad87e3ec7ed78e24e48f33b3312730de4e48675e89582aa561ddf50e7"}}
\ No newline at end of file
+{"package":null,"files":{".cargo-checksum.json":"11605a10285a8ed21fda31ba208e50306d7f2569fb4b3935d06964dc2fdf4415","Android.bp":"a34ee3fea0ad44c6b81bb02097dc8f4c9419e8828e013dd5555d5436e5685bc6","Cargo.toml":"fd7d6fff3744f32c795682a41adc114fc36631923ecb5421384577df49d86c16","LICENSE":"3c7cd2396b5b772507febd2615d3d5a55b80103845037df77c87ba6e64872f2c","LICENSE-APACHE":"3c7cd2396b5b772507febd2615d3d5a55b80103845037df77c87ba6e64872f2c","LICENSE-MIT":"3b270c5247fc2265dac124e9ada8609aa00c03289761bafa13c741aee60a504a","METADATA":"12dfe0f773e9ac7f603027bfeaeb396873bc7cfc108f048f5fe3a430d263eccb","MODULE_LICENSE_APACHE2":"0d6f8afa3940b7f06bebee651376d43bc8b0d5b437337be2696d30377451e93a","README.md":"94b361e8062eac8c5297a235595146e0758c2fe96a573776ae6022bcaf67508a","TEST_MAPPING":"02e3608ad87e3ec7ed78e24e48f33b3312730de4e48675e89582aa561ddf50e7","cargo_embargo.json":"aa45a963da01d3f018be316cd5b7646a5b413ce2611c5218f2914d2e8a9efd0e","src/container_attributes.rs":"b3132133bb66fa6f34ca83d6f2c4e8e8c85b7719ddf41b33ab4724e5af38b722","src/field_attributes.rs":"cdb0ba9cd0b22fa3b6e2df50f86e777347940a731ac460312e64cd68ba11d13c","src/lib.rs":"ff97d54d566d39a62d5d1e9bd037081c5f51267f7a5681dc6bedc1f4fc5f6128","src/variant_attributes.rs":"28cca42e56d167dff434c0fe2e0f07ba1bc72039738c55b59d4a5502cf9bea3b"}}
\ No newline at end of file
diff --git a/crates/derive_arbitrary/.cargo-checksum.json b/crates/derive_arbitrary/.cargo-checksum.json
index 00d7243..9c165e8 100644
--- a/crates/derive_arbitrary/.cargo-checksum.json
+++ b/crates/derive_arbitrary/.cargo-checksum.json
@@ -1 +1 @@
-{"files":{"Cargo.toml":"196abb2a00d047e16ba1337cd492b4115ebcee941283acde6fb80dc025176dce","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"15656cc11a8331f28c0986b8ab97220d3e76f98e60ed388b5ffad37dfac4710c","README.md":"7059db284b2016ba7355c63a2b14eb732c7b8952286ff1bc4fdde605018a39c4","src/container_attributes.rs":"9342a89e5e5f412159d1a1a88ae4ee0248180f30adc13e61ebf5f96b5f09877f","src/field_attributes.rs":"15093171d7f1e30c2b2523788e54c69c816029b310133ceb1ac811d1f11a76d5","src/lib.rs":"701a9c66e25c3a2151eab9159f6c2fa64ea910ceb6feb225dc97c2734254c265"},"package":"67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611"}
\ No newline at end of file
+{"files":{"Cargo.toml":"f8a5e6d7f00bfbf9ed898cdb4164d04df28887c1cb9903cd8009c4e34a1be814","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"15656cc11a8331f28c0986b8ab97220d3e76f98e60ed388b5ffad37dfac4710c","README.md":"7059db284b2016ba7355c63a2b14eb732c7b8952286ff1bc4fdde605018a39c4","src/container_attributes.rs":"9342a89e5e5f412159d1a1a88ae4ee0248180f30adc13e61ebf5f96b5f09877f","src/field_attributes.rs":"15093171d7f1e30c2b2523788e54c69c816029b310133ceb1ac811d1f11a76d5","src/lib.rs":"ecc4eddd09472d5e06eef8f6d0092bee896abcad168391e950e18cf383135e1c","src/variant_attributes.rs":"01428d7c9db83d06e6b846e9057352e830f9d45090c6cb21415426c4bab9df09"},"package":"30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800"}
\ No newline at end of file
diff --git a/crates/derive_arbitrary/Android.bp b/crates/derive_arbitrary/Android.bp
index 8144ca5..a8c2779 100644
--- a/crates/derive_arbitrary/Android.bp
+++ b/crates/derive_arbitrary/Android.bp
@@ -17,7 +17,7 @@
     name: "libderive_arbitrary",
     crate_name: "derive_arbitrary",
     cargo_env_compat: true,
-    cargo_pkg_version: "1.3.2",
+    cargo_pkg_version: "1.4.1",
     crate_root: "src/lib.rs",
     edition: "2021",
     rustlibs: [
diff --git a/crates/derive_arbitrary/Cargo.toml b/crates/derive_arbitrary/Cargo.toml
index 46e86fd..5a06e0c 100644
--- a/crates/derive_arbitrary/Cargo.toml
+++ b/crates/derive_arbitrary/Cargo.toml
@@ -13,7 +13,7 @@
 edition = "2021"
 rust-version = "1.63.0"
 name = "derive_arbitrary"
-version = "1.3.2"
+version = "1.4.1"
 authors = [
     "The Rust-Fuzz Project Developers",
     "Nick Fitzgerald <[email protected]>",
@@ -21,6 +21,11 @@
     "Andre Bogus <[email protected]>",
     "Corey Farwell <[email protected]>",
 ]
+build = false
+autobins = false
+autoexamples = false
+autotests = false
+autobenches = false
 description = "Derives arbitrary traits"
 documentation = "https://docs.rs/arbitrary/"
 readme = "README.md"
@@ -35,7 +40,9 @@
 repository = "https://github.com/rust-fuzz/arbitrary"
 
 [lib]
-proc_macro = true
+name = "derive_arbitrary"
+path = "src/lib.rs"
+proc-macro = true
 
 [dependencies.proc-macro2]
 version = "1.0"
@@ -48,4 +55,5 @@
 features = [
     "derive",
     "parsing",
+    "extra-traits",
 ]
diff --git a/crates/derive_arbitrary/METADATA b/crates/derive_arbitrary/METADATA
index 23ddd77..b24118b 100644
--- a/crates/derive_arbitrary/METADATA
+++ b/crates/derive_arbitrary/METADATA
@@ -1,17 +1,17 @@
 name: "derive_arbitrary"
 description: "Derives arbitrary traits"
 third_party {
-  version: "1.3.2"
+  version: "1.4.1"
   license_type: NOTICE
   last_upgrade_date {
-    year: 2024
-    month: 2
-    day: 8
+    year: 2025
+    month: 1
+    day: 7
   }
   homepage: "https://crates.io/crates/derive_arbitrary"
   identifier {
     type: "Archive"
-    value: "https://static.crates.io/crates/derive_arbitrary/derive_arbitrary-1.3.2.crate"
-    version: "1.3.2"
+    value: "https://static.crates.io/crates/derive_arbitrary/derive_arbitrary-1.4.1.crate"
+    version: "1.4.1"
   }
 }
diff --git a/crates/derive_arbitrary/src/lib.rs b/crates/derive_arbitrary/src/lib.rs
index 886bb49..81c0274 100644
--- a/crates/derive_arbitrary/src/lib.rs
+++ b/crates/derive_arbitrary/src/lib.rs
@@ -6,11 +6,14 @@
 
 mod container_attributes;
 mod field_attributes;
+mod variant_attributes;
+
 use container_attributes::ContainerAttributes;
 use field_attributes::{determine_field_constructor, FieldConstructor};
+use variant_attributes::not_skipped;
 
-static ARBITRARY_ATTRIBUTE_NAME: &str = "arbitrary";
-static ARBITRARY_LIFETIME_NAME: &str = "'arbitrary";
+const ARBITRARY_ATTRIBUTE_NAME: &str = "arbitrary";
+const ARBITRARY_LIFETIME_NAME: &str = "'arbitrary";
 
 #[proc_macro_derive(Arbitrary, attributes(arbitrary))]
 pub fn derive_arbitrary(tokens: proc_macro::TokenStream) -> proc_macro::TokenStream {
@@ -55,9 +58,9 @@
 
     Ok(quote! {
         const _: () = {
-            std::thread_local! {
+            ::std::thread_local! {
                 #[allow(non_upper_case_globals)]
-                static #recursive_count: std::cell::Cell<u32> = std::cell::Cell::new(0);
+                static #recursive_count: ::core::cell::Cell<u32> = ::core::cell::Cell::new(0);
             }
 
             #[automatically_derived]
@@ -201,80 +204,107 @@
         })
     }
 
+    fn arbitrary_variant(
+        index: u64,
+        enum_name: &Ident,
+        variant_name: &Ident,
+        ctor: TokenStream,
+    ) -> TokenStream {
+        quote! { #index => #enum_name::#variant_name #ctor }
+    }
+
+    fn arbitrary_enum_method(
+        recursive_count: &syn::Ident,
+        unstructured: TokenStream,
+        variants: &[TokenStream],
+    ) -> impl quote::ToTokens {
+        let count = variants.len() as u64;
+        with_recursive_count_guard(
+            recursive_count,
+            quote! {
+                // Use a multiply + shift to generate a ranged random number
+                // with slight bias. For details, see:
+                // https://lemire.me/blog/2016/06/30/fast-random-shuffling
+                Ok(match (u64::from(<u32 as arbitrary::Arbitrary>::arbitrary(#unstructured)?) * #count) >> 32 {
+                    #(#variants,)*
+                    _ => unreachable!()
+                })
+            },
+        )
+    }
+
+    fn arbitrary_enum(
+        DataEnum { variants, .. }: &DataEnum,
+        enum_name: &Ident,
+        lifetime: LifetimeParam,
+        recursive_count: &syn::Ident,
+    ) -> Result<TokenStream> {
+        let filtered_variants = variants.iter().filter(not_skipped);
+
+        // Check attributes of all variants:
+        filtered_variants
+            .clone()
+            .try_for_each(check_variant_attrs)?;
+
+        // From here on, we can assume that the attributes of all variants were checked.
+        let enumerated_variants = filtered_variants
+            .enumerate()
+            .map(|(index, variant)| (index as u64, variant));
+
+        // Construct `match`-arms for the `arbitrary` method.
+        let variants = enumerated_variants
+            .clone()
+            .map(|(index, Variant { fields, ident, .. })| {
+                construct(fields, |_, field| gen_constructor_for_field(field))
+                    .map(|ctor| arbitrary_variant(index, enum_name, ident, ctor))
+            })
+            .collect::<Result<Vec<TokenStream>>>()?;
+
+        // Construct `match`-arms for the `arbitrary_take_rest` method.
+        let variants_take_rest = enumerated_variants
+            .map(|(index, Variant { fields, ident, .. })| {
+                construct_take_rest(fields)
+                    .map(|ctor| arbitrary_variant(index, enum_name, ident, ctor))
+            })
+            .collect::<Result<Vec<TokenStream>>>()?;
+
+        // Most of the time, `variants` is not empty (the happy path),
+        //   thus `variants_take_rest` will be used,
+        //   so no need to move this check before constructing `variants_take_rest`.
+        // If `variants` is empty, this will emit a compiler-error.
+        (!variants.is_empty())
+            .then(|| {
+                // TODO: Improve dealing with `u` vs. `&mut u`.
+                let arbitrary = arbitrary_enum_method(recursive_count, quote! { u }, &variants);
+                let arbitrary_take_rest = arbitrary_enum_method(recursive_count, quote! { &mut u }, &variants_take_rest);
+
+                quote! {
+                    fn arbitrary(u: &mut arbitrary::Unstructured<#lifetime>) -> arbitrary::Result<Self> {
+                        #arbitrary
+                    }
+
+                    fn arbitrary_take_rest(mut u: arbitrary::Unstructured<#lifetime>) -> arbitrary::Result<Self> {
+                        #arbitrary_take_rest
+                    }
+                }
+            })
+            .ok_or_else(|| Error::new_spanned(
+                enum_name,
+                "Enum must have at least one variant, that is not skipped"
+            ))
+    }
+
     let ident = &input.ident;
-    let output = match &input.data {
-        Data::Struct(data) => arbitrary_structlike(&data.fields, ident, lifetime, recursive_count)?,
+    match &input.data {
+        Data::Struct(data) => arbitrary_structlike(&data.fields, ident, lifetime, recursive_count),
         Data::Union(data) => arbitrary_structlike(
             &Fields::Named(data.fields.clone()),
             ident,
             lifetime,
             recursive_count,
-        )?,
-        Data::Enum(data) => {
-            let variants: Vec<TokenStream> = data
-                .variants
-                .iter()
-                .enumerate()
-                .map(|(i, variant)| {
-                    let idx = i as u64;
-                    let variant_name = &variant.ident;
-                    construct(&variant.fields, |_, field| gen_constructor_for_field(field))
-                        .map(|ctor| quote! { #idx => #ident::#variant_name #ctor })
-                })
-                .collect::<Result<_>>()?;
-
-            let variants_take_rest: Vec<TokenStream> = data
-                .variants
-                .iter()
-                .enumerate()
-                .map(|(i, variant)| {
-                    let idx = i as u64;
-                    let variant_name = &variant.ident;
-                    construct_take_rest(&variant.fields)
-                        .map(|ctor| quote! { #idx => #ident::#variant_name #ctor })
-                })
-                .collect::<Result<_>>()?;
-
-            let count = data.variants.len() as u64;
-
-            let arbitrary = with_recursive_count_guard(
-                recursive_count,
-                quote! {
-                    // Use a multiply + shift to generate a ranged random number
-                    // with slight bias. For details, see:
-                    // https://lemire.me/blog/2016/06/30/fast-random-shuffling
-                    Ok(match (u64::from(<u32 as arbitrary::Arbitrary>::arbitrary(u)?) * #count) >> 32 {
-                        #(#variants,)*
-                        _ => unreachable!()
-                    })
-                },
-            );
-
-            let arbitrary_take_rest = with_recursive_count_guard(
-                recursive_count,
-                quote! {
-                    // Use a multiply + shift to generate a ranged random number
-                    // with slight bias. For details, see:
-                    // https://lemire.me/blog/2016/06/30/fast-random-shuffling
-                    Ok(match (u64::from(<u32 as arbitrary::Arbitrary>::arbitrary(&mut u)?) * #count) >> 32 {
-                        #(#variants_take_rest,)*
-                        _ => unreachable!()
-                    })
-                },
-            );
-
-            quote! {
-                fn arbitrary(u: &mut arbitrary::Unstructured<#lifetime>) -> arbitrary::Result<Self> {
-                    #arbitrary
-                }
-
-                fn arbitrary_take_rest(mut u: arbitrary::Unstructured<#lifetime>) -> arbitrary::Result<Self> {
-                    #arbitrary_take_rest
-                }
-            }
-        }
-    };
-    Ok(output)
+        ),
+        Data::Enum(data) => arbitrary_enum(data, ident, lifetime, recursive_count),
+    }
 }
 
 fn construct(
@@ -311,7 +341,7 @@
 fn construct_take_rest(fields: &Fields) -> Result<TokenStream> {
     construct(fields, |idx, field| {
         determine_field_constructor(field).map(|field_constructor| match field_constructor {
-            FieldConstructor::Default => quote!(Default::default()),
+            FieldConstructor::Default => quote!(::core::default::Default::default()),
             FieldConstructor::Arbitrary => {
                 if idx + 1 == fields.len() {
                     quote! { arbitrary::Arbitrary::arbitrary_take_rest(u)? }
@@ -334,17 +364,17 @@
                 determine_field_constructor(f).map(|field_constructor| {
                     match field_constructor {
                         FieldConstructor::Default | FieldConstructor::Value(_) => {
-                            quote!((0, Some(0)))
+                            quote!(Ok((0, Some(0))))
                         }
                         FieldConstructor::Arbitrary => {
-                            quote! { <#ty as arbitrary::Arbitrary>::size_hint(depth) }
+                            quote! { <#ty as arbitrary::Arbitrary>::try_size_hint(depth) }
                         }
 
                         // Note that in this case it's hard to determine what size_hint must be, so size_of::<T>() is
                         // just an educated guess, although it's gonna be inaccurate for dynamically
                         // allocated types (Vec, HashMap, etc.).
                         FieldConstructor::With(_) => {
-                            quote! { (::core::mem::size_of::<#ty>(), None) }
+                            quote! { Ok((::core::mem::size_of::<#ty>(), None)) }
                         }
                     }
                 })
@@ -352,9 +382,9 @@
             .collect::<Result<Vec<TokenStream>>>()
             .map(|hints| {
                 quote! {
-                    arbitrary::size_hint::and_all(&[
-                        #( #hints ),*
-                    ])
+                    Ok(arbitrary::size_hint::and_all(&[
+                        #( #hints? ),*
+                    ]))
                 }
             })
     };
@@ -362,8 +392,13 @@
         size_hint_fields(fields).map(|hint| {
             quote! {
                 #[inline]
-                fn size_hint(depth: usize) -> (usize, Option<usize>) {
-                    arbitrary::size_hint::recursion_guard(depth, |depth| #hint)
+                fn size_hint(depth: usize) -> (usize, ::core::option::Option<usize>) {
+                    Self::try_size_hint(depth).unwrap_or_default()
+                }
+
+                #[inline]
+                fn try_size_hint(depth: usize) -> ::core::result::Result<(usize, ::core::option::Option<usize>), arbitrary::MaxRecursionReached> {
+                    arbitrary::size_hint::try_recursion_guard(depth, |depth| #hint)
                 }
             }
         })
@@ -374,18 +409,26 @@
         Data::Enum(data) => data
             .variants
             .iter()
-            .map(|v| size_hint_fields(&v.fields))
+            .filter(not_skipped)
+            .map(|Variant { fields, .. }| {
+                // The attributes of all variants are checked in `gen_arbitrary_method` above
+                //   and can therefore assume that they are valid.
+                size_hint_fields(fields)
+            })
             .collect::<Result<Vec<TokenStream>>>()
             .map(|variants| {
                 quote! {
+                    fn size_hint(depth: usize) -> (usize, ::core::option::Option<usize>) {
+                        Self::try_size_hint(depth).unwrap_or_default()
+                    }
                     #[inline]
-                    fn size_hint(depth: usize) -> (usize, Option<usize>) {
-                        arbitrary::size_hint::and(
-                            <u32 as arbitrary::Arbitrary>::size_hint(depth),
-                            arbitrary::size_hint::recursion_guard(depth, |depth| {
-                                arbitrary::size_hint::or_all(&[ #( #variants ),* ])
-                            }),
-                        )
+                    fn try_size_hint(depth: usize) -> ::core::result::Result<(usize, ::core::option::Option<usize>), arbitrary::MaxRecursionReached> {
+                        Ok(arbitrary::size_hint::and(
+                            <u32 as arbitrary::Arbitrary>::try_size_hint(depth)?,
+                            arbitrary::size_hint::try_recursion_guard(depth, |depth| {
+                                Ok(arbitrary::size_hint::or_all(&[ #( #variants? ),* ]))
+                            })?,
+                        ))
                     }
                 }
             }),
@@ -394,10 +437,25 @@
 
 fn gen_constructor_for_field(field: &Field) -> Result<TokenStream> {
     let ctor = match determine_field_constructor(field)? {
-        FieldConstructor::Default => quote!(Default::default()),
+        FieldConstructor::Default => quote!(::core::default::Default::default()),
         FieldConstructor::Arbitrary => quote!(arbitrary::Arbitrary::arbitrary(u)?),
         FieldConstructor::With(function_or_closure) => quote!((#function_or_closure)(u)?),
         FieldConstructor::Value(value) => quote!(#value),
     };
     Ok(ctor)
 }
+
+fn check_variant_attrs(variant: &Variant) -> Result<()> {
+    for attr in &variant.attrs {
+        if attr.path().is_ident(ARBITRARY_ATTRIBUTE_NAME) {
+            return Err(Error::new_spanned(
+                attr,
+                format!(
+                    "invalid `{}` attribute. it is unsupported on enum variants. try applying it to a field of the variant instead",
+                    ARBITRARY_ATTRIBUTE_NAME
+                ),
+            ));
+        }
+    }
+    Ok(())
+}
diff --git a/crates/derive_arbitrary/src/variant_attributes.rs b/crates/derive_arbitrary/src/variant_attributes.rs
new file mode 100644
index 0000000..39957df
--- /dev/null
+++ b/crates/derive_arbitrary/src/variant_attributes.rs
@@ -0,0 +1,21 @@
+use crate::ARBITRARY_ATTRIBUTE_NAME;
+use syn::*;
+
+pub fn not_skipped(variant: &&Variant) -> bool {
+    !should_skip(variant)
+}
+
+fn should_skip(Variant { attrs, .. }: &Variant) -> bool {
+    attrs
+        .iter()
+        .filter_map(|attr| {
+            attr.path()
+                .is_ident(ARBITRARY_ATTRIBUTE_NAME)
+                .then(|| attr.parse_args::<Meta>())
+                .and_then(Result::ok)
+        })
+        .any(|meta| match meta {
+            Meta::Path(path) => path.is_ident("skip"),
+            _ => false,
+        })
+}
diff --git a/pseudo_crate/Cargo.lock b/pseudo_crate/Cargo.lock
index f91f7bd..39eaecd 100644
--- a/pseudo_crate/Cargo.lock
+++ b/pseudo_crate/Cargo.lock
@@ -606,9 +606,9 @@
 
 [[package]]
 name = "arbitrary"
-version = "1.2.3"
+version = "1.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3e90af4de65aa7b293ef2d09daff88501eb254f58edde2e1ac02c82d873eadad"
+checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223"
 
 [[package]]
 name = "arc-swap"
@@ -1645,9 +1645,9 @@
 
 [[package]]
 name = "derive_arbitrary"
-version = "1.3.2"
+version = "1.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611"
+checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800"
 dependencies = [
  "proc-macro2 1.0.92",
  "quote 1.0.37",
@@ -1709,7 +1709,7 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412"
 dependencies = [
- "libloading 0.7.4",
+ "libloading 0.8.6",
 ]
 
 [[package]]
diff --git a/pseudo_crate/Cargo.toml b/pseudo_crate/Cargo.toml
index ceec3fa..419e9da 100644
--- a/pseudo_crate/Cargo.toml
+++ b/pseudo_crate/Cargo.toml
@@ -16,7 +16,7 @@
 annotate-snippets = "=0.9.2"
 anstyle = "=1.0.10"
 anyhow = "=1.0.79"
-arbitrary = "=1.2.3"
+arbitrary = "=1.4.1"
 arc-swap = "=1.7.1"
 argh = "=0.1.12"
 argh_derive = "=0.1.12"
@@ -86,7 +86,7 @@
 debug_tree = "=0.4.0"
 der = "=0.7.9"
 der_derive = "=0.7.3"
-derive_arbitrary = "=1.3.2"
+derive_arbitrary = "=1.4.1"
 displaydoc = "=0.2.5"
 document-features = "=0.2.10"
 downcast = "=0.11.0"