No public description

PiperOrigin-RevId: 903228420
diff --git a/rust/e2e_tests/testdata/fuzz_tests.rs b/rust/e2e_tests/testdata/fuzz_tests.rs
index 9b827a9..d4f8431 100644
--- a/rust/e2e_tests/testdata/fuzz_tests.rs
+++ b/rust/e2e_tests/testdata/fuzz_tests.rs
@@ -32,14 +32,14 @@
     type UserValue<'user> = Vec<u8>;
     type CorpusValue = Vec<u8>;
 
-    fn init(&self, rng: &mut dyn rand::Rng) -> anyhow::Result<Self::CorpusValue> {
+    fn init(&mut self, rng: &mut dyn rand::Rng) -> anyhow::Result<Self::CorpusValue> {
         let mut val = vec![0u8; rng.random_range(0..100)];
         rng.fill(&mut val[..]);
         Ok(val)
     }
 
     fn mutate(
-        &self,
+        &mut self,
         val: &mut Self::CorpusValue,
         rng: &mut dyn rand::Rng,
         only_shrink: bool,
@@ -78,6 +78,10 @@
     ) -> anyhow::Result<Self::UserValue<'a>> {
         Ok(val.clone())
     }
+
+    fn from_value(&self, value: Self::UserValue<'_>) -> anyhow::Result<Self::CorpusValue> {
+        Ok(value)
+    }
 }
 
 #[fuzztest(_a = Arbitrary::<bool>::default())]
@@ -160,12 +164,12 @@
     type UserValue<'user> = u32;
     type CorpusValue = u32;
 
-    fn init(&self, _rng: &mut dyn rand::Rng) -> anyhow::Result<Self::CorpusValue> {
+    fn init(&mut self, _rng: &mut dyn rand::Rng) -> anyhow::Result<Self::CorpusValue> {
         Ok(0)
     }
 
     fn mutate(
-        &self,
+        &mut self,
         _val: &mut Self::CorpusValue,
         _rng: &mut dyn rand::Rng,
         _only_shrink: bool,
@@ -179,6 +183,10 @@
     ) -> anyhow::Result<Self::UserValue<'a>> {
         Ok(*val)
     }
+
+    fn from_value(&self, value: Self::UserValue<'_>) -> anyhow::Result<Self::CorpusValue> {
+        Ok(value)
+    }
 }
 
 #[fuzztest(a = FallibleDomain::new())]
diff --git a/rust/fuzztest_macro/src/helpers/fuzztest_domain.rs b/rust/fuzztest_macro/src/helpers/fuzztest_domain.rs
index 6cb6b58..3cb112a 100644
--- a/rust/fuzztest_macro/src/helpers/fuzztest_domain.rs
+++ b/rust/fuzztest_macro/src/helpers/fuzztest_domain.rs
@@ -77,14 +77,14 @@
         type UserValue<#user_value_lifetime_generic> = #domain_struct_name <#(#user_value_domain_generics),*>;
         type CorpusValue = #domain_struct_name <#(#corpus_domain_generics),*>;
 
-        fn init(&self, rng: &mut dyn ::fuzztest::reexports::rand::Rng) -> ::fuzztest::reexports::anyhow::Result<Self::CorpusValue> {
+        fn init(&mut self, rng: &mut dyn ::fuzztest::reexports::rand::Rng) -> ::fuzztest::reexports::anyhow::Result<Self::CorpusValue> {
           Ok(#domain_struct_name {
             #(#field_names: self.#field_names.init(rng)?),*
           })
         }
 
         fn mutate(
-            &self,
+            &mut self,
             val: &mut Self::CorpusValue,
             rng: &mut dyn ::fuzztest::reexports::rand::Rng,
             only_shrink: bool,
@@ -98,6 +98,17 @@
             #(#field_names: self.#field_names.get_user_value(&corpus_value.#field_names)?),*
           })
         }
+
+        fn from_value(&self, value: Self::UserValue<'_>) -> ::fuzztest::reexports::anyhow::Result<Self::CorpusValue> {
+          Ok(#domain_struct_name {
+            #(#field_names: self.#field_names.from_value(value.#field_names)?),*
+          })
+        }
+
+        fn validate_corpus_value(&self, corpus_value: &Self::CorpusValue) -> ::fuzztest::reexports::anyhow::Result<()> {
+          #( self.#field_names.validate_corpus_value(&corpus_value.#field_names)?; )*
+          Ok(())
+        }
       }
     };
     (domain_definition_tokens, field_names)
@@ -144,7 +155,7 @@
               type UserValue<'user> = __FuzzTestTestFuzzStateWrapper<T0::UserValue<'user>, T1::UserValue<'user> >;
               type CorpusValue = __FuzzTestTestFuzzStateWrapper<T0::CorpusValue, T1::CorpusValue>;
 
-              fn init(&self, rng: &mut dyn ::fuzztest::reexports::rand::Rng) -> ::fuzztest::reexports::anyhow::Result<Self::CorpusValue> {
+              fn init(&mut self, rng: &mut dyn ::fuzztest::reexports::rand::Rng) -> ::fuzztest::reexports::anyhow::Result<Self::CorpusValue> {
                 Ok(__FuzzTestTestFuzzStateWrapper {
                   a: self.a.init(rng)?,
                   b: self.b.init(rng)?
@@ -152,7 +163,7 @@
               }
 
               fn mutate(
-                  &self,
+                  &mut self,
                   val: &mut Self::CorpusValue,
                   rng: &mut dyn ::fuzztest::reexports::rand::Rng,
                   only_shrink: bool,
@@ -168,6 +179,19 @@
                   b: self.b.get_user_value(&corpus_value.b)?
                 })
               }
+
+              fn from_value(&self, value: Self::UserValue<'_>) -> ::fuzztest::reexports::anyhow::Result<Self::CorpusValue> {
+                Ok(__FuzzTestTestFuzzStateWrapper {
+                  a: self.a.from_value(value.a)?,
+                  b: self.b.from_value(value.b)?
+                })
+              }
+
+              fn validate_corpus_value(&self, corpus_value: &Self::CorpusValue) -> ::fuzztest::reexports::anyhow::Result<()> {
+                self.a.validate_corpus_value(&corpus_value.a)?;
+                self.b.validate_corpus_value(&corpus_value.b)?;
+                Ok(())
+              }
             }
           }
           .to_string())
diff --git a/rust/fuzztest_macro/src/helpers/test_registration.rs b/rust/fuzztest_macro/src/helpers/test_registration.rs
index 3806469..bf7be38 100644
--- a/rust/fuzztest_macro/src/helpers/test_registration.rs
+++ b/rust/fuzztest_macro/src/helpers/test_registration.rs
@@ -109,9 +109,11 @@
 
         let fuzz_test_struct_instance_tokens = quote!(
           #fuzz_test_struct_name {
-              domain: #domain_struct_name {
+              domain: std::sync::Arc::new(std::sync::Mutex::new(
+                #domain_struct_name {
                   #(#fuzz_test_domain_field_names: #domain_ctors),*
-              },
+                }
+              )),
               test_fn: #prop_fn_ident
           }
         );
@@ -212,7 +214,7 @@
         {
             where_clauses.predicates.push(
           parse_quote! {
-            for <#user_value_lifetime_generic> #domain_gen: #crate_name::domains::Domain<UserValue<#user_value_lifetime_generic> = #ty >
+            for <#user_value_lifetime_generic> #domain_gen: #crate_name::domains::Domain<UserValue<#user_value_lifetime_generic> = #ty > + 'static
         });
             where_clauses.predicates.push(parse_quote! { #corpus_gen: 'static });
         }
@@ -227,7 +229,7 @@
           #fuzz_test_domain_definition
 
           struct #fuzz_test_struct_name #generics {
-            domain: #domain_struct_name #generics,
+            domain: std::sync::Arc<std::sync::Mutex<#domain_struct_name #generics>>,
             test_fn: #test_fn_type
           }
 
@@ -248,7 +250,10 @@
                                 .downcast_ref::<#domain_struct_name<#(#corpus_generics),*>>()
                                 .expect("Attempt to recover user value before testing failed.");
 
-                  let user_value = self.domain.get_user_value(wrapper).expect("Failed to get user value from corpus value");
+                  let user_value = self.domain.lock()
+                  .expect("Failed to acquire domain lock")
+                  .get_user_value(wrapper)
+                  .expect("Failed to get user value from corpus value");
 
                   let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| (self.test_fn)(#(user_value.#fuzz_test_domain_field_names),* ) ));
 
@@ -257,8 +262,8 @@
               fn print_finding_report(&self) {
                   todo!("Not implemented!")
               }
-              fn domains(&self) -> &dyn #crate_name::domains::GenericDomain {
-                &self.domain
+              fn domains(&self) -> std::sync::Arc<std::sync::Mutex<dyn #crate_name::domains::GenericDomain>> {
+                std::sync::Arc::clone(&self.domain) as std::sync::Arc<std::sync::Mutex<dyn #crate_name::domains::GenericDomain>>
               }
           }
 
@@ -299,14 +304,14 @@
         expect_that!(
           fuzztest_object_tokenstream.to_string(), ends_with( quote! {
               struct __FuzzTestTestFuzz<T0, T1> {
-                domain: __FuzzTestTestFuzzStateWrapper<T0, T1>,
+                domain: std::sync::Arc<std::sync::Mutex<__FuzzTestTestFuzzStateWrapper<T0, T1> >>,
                 test_fn: fn(i32, std::string::String)
               }
 
               impl<T0, T1> ::fuzztest::internal::FuzzTest for __FuzzTestTestFuzz<T0, T1>
-              where for <'user> T0: ::fuzztest::domains::Domain<UserValue<'user> = i32>,
+              where for <'user> T0: ::fuzztest::domains::Domain<UserValue<'user> = i32> + 'static,
                     T0::CorpusValue: 'static,
-                    for <'user> T1: ::fuzztest::domains::Domain<UserValue<'user> = std::string::String>,
+                    for <'user> T1: ::fuzztest::domains::Domain<UserValue<'user> = std::string::String> + 'static,
                     T1::CorpusValue: 'static {
                   fn name(&self) -> &'static str {
                     "test_fuzz"
@@ -324,7 +329,10 @@
                             .downcast_ref::<__FuzzTestTestFuzzStateWrapper<T0::CorpusValue, T1::CorpusValue>>()
                             .expect("Attempt to recover user value before testing failed.");
 
-                    let user_value = self.domain.get_user_value(wrapper).expect("Failed to get user value from corpus value");
+                    let user_value = self.domain.lock()
+                        .expect("Failed to acquire domain lock")
+                        .get_user_value(wrapper)
+                        .expect("Failed to get user value from corpus value");
                     // Safety: Data is not reused after the test.
                     let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| (self.test_fn)(user_value.a, user_value.b) ));
 
@@ -333,17 +341,19 @@
                   fn print_finding_report(&self) {
                     todo!("Not implemented!")
                   }
-                  fn domains(&self) -> &dyn ::fuzztest::domains::GenericDomain {
-                    &self.domain
+                  fn domains(&self) -> std::sync::Arc<std::sync::Mutex<dyn ::fuzztest::domains::GenericDomain>> {
+                    std::sync::Arc::clone(&self.domain) as std::sync::Arc<std::sync::Mutex<dyn ::fuzztest::domains::GenericDomain>>
                   }
               }
 
               fn __FuzzTestTestFuzz_factory() -> ::fuzztest::internal::BoxedFuzzTest {
                 ::std::boxed::Box::new(__FuzzTestTestFuzz {
-                  domain: __FuzzTestTestFuzzStateWrapper {
-                    a: ::fuzztest::domains::arbitrary::Arbitrary::<i32>::default(),
-                    b: ::fuzztest::domains::arbitrary::Arbitrary::<String>::default()
-                  },
+                  domain: std::sync::Arc::new(std::sync::Mutex::new(
+                    __FuzzTestTestFuzzStateWrapper {
+                      a: ::fuzztest::domains::arbitrary::Arbitrary::<i32>::default(),
+                      b: ::fuzztest::domains::arbitrary::Arbitrary::<String>::default()
+                    }
+                  )),
                   test_fn: __property_fn__test_fuzz
                 })
               }
diff --git a/rust/src/domains.rs b/rust/src/domains.rs
index a873c59..8fbf5a6 100644
--- a/rust/src/domains.rs
+++ b/rust/src/domains.rs
@@ -32,17 +32,10 @@
 }
 
 impl dyn CloneAny {
-    /// Returns `true` if the underlying type is of type `T`
-    pub fn is<T: Any>(&self) -> bool {
-        self.as_any().is::<T>()
-    }
-
-    /// Returns a reference to the concrete value if it is of type `T`, or `None` otherwise.
     pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
         self.as_any().downcast_ref::<T>()
     }
 
-    /// Returns a mutable reference to the concrete value if it is of type `T`, or `None` otherwise.
     pub fn downcast_mut<T: Any>(&mut self) -> Option<&mut T> {
         self.as_mut_any().downcast_mut::<T>()
     }
@@ -50,7 +43,6 @@
 
 impl<T: Any + Clone> CloneAny for T {
     fn clone_box(&self) -> Box<dyn CloneAny> {
-        // Prevent infinite recursion if T is a nested Box type.
         if let Some(boxed) = self.as_any().downcast_ref::<Box<dyn CloneAny>>() {
             return (**boxed).clone_box();
         }
@@ -78,143 +70,73 @@
 /// Type alias for a type-erased user value.
 pub type GenericUserValue = Box<dyn CloneAny>;
 
-/// A trait for types that represent a set of values that an input can take.
-///
-/// Domain types are used to represent the domain of values that a fuzz test input can take
-/// throughout the fuzzing process.
-/// Domain types are used by the fuzzing engine through the APIs defined in this trait.
-///
-/// ## Serialization/Deserialization of CorpusValue and UserValue relationship.
-///
-/// The values drawn from a domain are always of type `CorpusValue`. These values are the
-/// internal representation of the value and they are not directly usable by the fuzz property
-/// function.
-///
-/// The `get_user_value` method is used to retrieve the user value from the corpus value. For
-/// example, if the domain outputs an `&str` then the CorpusValue could be a `String` and the
-/// `get_user_value` method would be used to retrieve the `&str` from the `String`.
-///
-/// The `parse_corpus` (resp. `serialize_corpus`) method is used deserialize (resp. serialize)
-/// the corpus value from (to) a slice of bytes (resp. a vector of bytes).
-///
-/// ### Relationship diagram
-///
-/// The methods below are responsible for transforming between `UserValue`, `CorpusValue`, and the
-/// serialized representation of `CorpusValue`. Here's a quick overview:
-///
-/// ```text
-///        +-- get_user_value() <---+     +-- parse_corpus() <---+
-///        |                        |     |                      |
-///        v                        |     v                      |
-///   UserValue<'a>               CorpusValue                  &[u8]
-///                                       |                      ^
-///                                       |                      |
-///                                       +-> serialize_corpus() +
-/// ```
-pub trait Domain {
-    /// The type of the values that the domain outputs. This should of the same type as the
-    /// parameter of the fuzz property function.
+pub trait Domain: 'static {
+    /// The user-facing type representing values in this domain.
     type UserValue<'user>;
-    /// The type of the corpus from which the values of type UserValue are drawn.
-    /// For example, if the domain should output an `&str` then the UserValue should be `&str` and
-    /// the CorpusValue could the owned data structured that the `&str` points to (eg: String).
-    /// The CorpusValue type should implement `serde::Serialize`, `serde::de::DeserializeOwned` and
-    /// `Clone`.
+
+    /// The type representing the value stored in the corpus for this domain.
     type CorpusValue: Serialize + DeserializeOwned + Clone;
 
-    /// Initializes a new value drawn from the domain.
-    fn init(&self, rng: &mut dyn rand::Rng) -> anyhow::Result<Self::CorpusValue>;
+    /// Produces a new initial `CorpusValue` for this domain.
+    fn init(&mut self, rng: &mut dyn rand::Rng) -> anyhow::Result<Self::CorpusValue>;
 
-    /// Mutates the value in `val` to a new value drawn from the domain.
-    ///
-    /// If `only_shrink` is `true`, then the mutation must not increase the size of the corpus
-    /// value. Otherwise, the mutation can both shrink and grow the corpus value.
+    /// Mutates the given `corpus_value` in place.
     fn mutate(
-        &self,
-        val: &mut Self::CorpusValue,
+        &mut self,
+        corpus_value: &mut Self::CorpusValue,
         rng: &mut dyn rand::Rng,
         only_shrink: bool,
     ) -> anyhow::Result<()>;
 
-    /// Retrieves a UserValue from a given CorpusValue.
-    ///
-    /// This is used to convert the corpus value into the user value that can then be passed to the
-    /// fuzz property function.
+    /// Obtains a `UserValue` from a `corpus_value`.
     fn get_user_value<'a>(
         &self,
         corpus_value: &'a Self::CorpusValue,
     ) -> anyhow::Result<Self::UserValue<'a>>;
 
-    /// Turns a slice of bytes into `CorpusValue`.
-    ///
-    /// By default, it uses Postcard to deserialize the data.
+    /// Deserializes a `CorpusValue` from a byte slice.
     fn parse_corpus(&self, data: &[u8]) -> anyhow::Result<Self::CorpusValue> {
         postcard::from_bytes(data).context("Failed to deserialize corpus value from bytes")
     }
 
     /// Serializes `corpus_value` to a Vec of bytes (ie, Vec<u8>).
-    ///
-    /// By default, it uses Postcard to serialize the data.
     fn serialize_corpus(&self, corpus_value: &Self::CorpusValue) -> anyhow::Result<Vec<u8>> {
         postcard::to_stdvec(corpus_value).context("Failed to serialize corpus value to bytes")
     }
+
+    /// Converts a user value to a corpus value.
+    #[allow(clippy::wrong_self_convention)]
+    fn from_value(&self, value: Self::UserValue<'_>) -> anyhow::Result<Self::CorpusValue>;
+
+    /// Validates that a corpus value satisfies the domain's constraints.
+    fn validate_corpus_value(&self, _corpus_value: &Self::CorpusValue) -> anyhow::Result<()> {
+        Ok(())
+    }
 }
 
-/// A type-erased interface for Domain types.
-///
-/// This trait is used to expose a common interface for Domain types to the fuzzing engine through
-/// the `FuzzTest` trait.
-/// The methods of this trait are similar to that of the `Domain` trait, but they operate on
-/// a type-erased `GenericCorpusValue` instead of an explicit type.
 pub trait GenericDomain {
-    /// Initializes a new value drawn from the domain.
-    ///
-    /// See `Domain::init` for more details.
-    fn init(&self, rng: &mut dyn rand::Rng) -> anyhow::Result<GenericCorpusValue>;
-
-    /// Mutates the value in `val` to a new value drawn from the domain.
-    ///
-    /// See `Domain::mutate` for more details.
+    fn init(&mut self, rng: &mut dyn rand::Rng) -> anyhow::Result<GenericCorpusValue>;
     fn mutate(
-        &self,
+        &mut self,
         val: &mut GenericCorpusValue,
         rng: &mut dyn rand::Rng,
         only_shrink: bool,
     ) -> anyhow::Result<()>;
-
-    /// Parses a slice of bytes into a `GenericCorpusValue`.
-    ///
-    /// See `Domain::parse_corpus` for more details.
     fn parse_corpus(&self, data: &[u8]) -> anyhow::Result<GenericCorpusValue>;
-
-    /// Serializes a `GenericCorpusValue` to a Vec of bytes (ie, Vec<u8>).
-    ///
-    /// See `Domain::serialize_corpus` for more details.
     fn serialize_corpus(&self, val: &GenericCorpusValue) -> anyhow::Result<Vec<u8>>;
 }
 
-/// Blanket implementation of the `GenericDomain` trait for types implementing the `Domain` trait.
-///
-/// This implementation is a simple pass-through implementation that uses the methods of the
-/// `Domain` trait to implement the `GenericDomain` trait.
 impl<D> GenericDomain for D
 where
     D: Domain,
     D::CorpusValue: 'static,
 {
-    fn init(&self, rng: &mut dyn rand::Rng) -> anyhow::Result<GenericCorpusValue> {
+    fn init(&mut self, rng: &mut dyn rand::Rng) -> anyhow::Result<GenericCorpusValue> {
         Ok(Box::new(self.init(rng)?))
     }
 
-    /// Retrieves the underlying `Domain`'s `CorpusValue` from the `GenericCorpusValue` and calls
-    /// the underlying `Domain::mutate` method.
-    ///
-    /// If the `GenericCorpusValue` cannot be downcasted to the underlying `Domain`'s `CorpusValue`
-    /// then an Error is returned.
-    ///
-    /// See `GenericDomain::mutate` for more details.
     fn mutate(
-        &self,
+        &mut self,
         val: &mut GenericCorpusValue,
         rng: &mut dyn rand::Rng,
         only_shrink: bool,
@@ -226,21 +148,10 @@
         )
     }
 
-    /// Calls the underlying `Domain::parse_corpus` method with the `data` and wraps the resulting
-    /// `CorpusValue` in a `GenericCorpusValue`.
-    ///
-    /// See `GenericDomain::parse_corpus` for more details.
     fn parse_corpus(&self, data: &[u8]) -> anyhow::Result<GenericCorpusValue> {
         Ok(Box::new(self.parse_corpus(data)?))
     }
 
-    /// Retrieves the underlying `Domain`'s `CorpusValue` from the `GenericCorpusValue` and calls
-    /// the underlying `Domain::serialize_corpus` method.
-    ///
-    /// If the `GenericCorpusValue` cannot be downcasted to the underlying `Domain`'s `CorpusValue`
-    /// then an Error is returned.
-    ///
-    /// See `GenericDomain::serialize_corpus` for more details.
     fn serialize_corpus(&self, val: &GenericCorpusValue) -> anyhow::Result<Vec<u8>> {
         self.serialize_corpus(val.downcast_ref().context("Failed to retrieve the Corpus Value")?)
     }
diff --git a/rust/src/domains/arbitrary.rs b/rust/src/domains/arbitrary.rs
index e2167bf..7872c0d 100644
--- a/rust/src/domains/arbitrary.rs
+++ b/rust/src/domains/arbitrary.rs
@@ -12,6 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+
 use super::utility::choose_value;
 use super::utility::mutate_integer;
 use super::utility::shrink_towards;
@@ -36,7 +37,7 @@
 /// # use rand::rngs::SmallRng;
 /// # use rand::SeedableRng;
 ///
-/// let arbitrary_i32 = Arbitrary::<i32>::default();
+/// let mut arbitrary_i32 = Arbitrary::<i32>::default();
 /// let mut rng = SmallRng::seed_from_u64(73);
 ///
 /// let sample = arbitrary_i32.init(&mut rng);
@@ -46,29 +47,35 @@
     _phantom: PhantomData<T>,
 }
 
-impl<T> Clone for Arbitrary<T> {
+impl<T: Clone> Clone for Arbitrary<T> {
     fn clone(&self) -> Self {
-        Self { _phantom: PhantomData }
+        Self {
+            _phantom: PhantomData,
+        }
     }
 }
 
-impl<T> fmt::Debug for Arbitrary<T> {
+impl<T: fmt::Debug> fmt::Debug for Arbitrary<T> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.debug_struct("Arbitrary").field("_phantom", &self._phantom).finish()
+        f.debug_struct("Arbitrary")
+            .field("_phantom", &self._phantom)
+            .finish()
     }
 }
 
 // We cannot just use `#[derive(Default)]` because `T` might not be `Default`.
 impl<T> Default for Arbitrary<T> {
     fn default() -> Self {
-        Self { _phantom: PhantomData }
+        Self {
+            _phantom: PhantomData,
+        }
     }
 }
 
 impl<T> Arbitrary<T> {
     /// Creates a new `Arbitrary` domain for the given type `T`.
     pub fn new() -> Self {
-        Self { _phantom: PhantomData }
+        Self::default()
     }
 }
 
@@ -76,12 +83,12 @@
     type UserValue<'user> = bool;
     type CorpusValue = bool;
 
-    fn init(&self, rng: &mut dyn rand::Rng) -> anyhow::Result<Self::CorpusValue> {
+    fn init(&mut self, rng: &mut dyn rand::Rng) -> anyhow::Result<Self::CorpusValue> {
         Ok(rng.random())
     }
 
     fn mutate(
-        &self,
+        &mut self,
         val: &mut Self::CorpusValue,
         rng: &mut dyn rand::Rng,
         only_shrink: bool,
@@ -101,6 +108,14 @@
     ) -> anyhow::Result<Self::UserValue<'a>> {
         Ok(*corpus_value)
     }
+
+    fn from_value(&self, value: Self::UserValue<'_>) -> anyhow::Result<Self::CorpusValue> {
+        Ok(value)
+    }
+
+    fn validate_corpus_value(&self, _corpus_value: &Self::CorpusValue) -> anyhow::Result<()> {
+        Ok(())
+    }
 }
 
 macro_rules! impl_domain_for_integer {
@@ -112,14 +127,14 @@
             type UserValue<'user> = $ty;
             type CorpusValue = $ty;
 
-            fn init(&self, rng: &mut dyn rand::Rng) -> anyhow::Result<Self::CorpusValue> {
-                // We generate a the equivalent integer type so this works for size types.
+            fn init(&mut self, rng: &mut dyn rand::Rng) -> anyhow::Result<Self::CorpusValue> {
+                // We generate the equivalent integer type so this works for size types.
                 let val: $int_ty = choose_value(rng);
                 Ok(val as $ty)
             }
 
             fn mutate(
-                &self,
+                &mut self,
                 val: &mut Self::CorpusValue,
                 rng: &mut dyn rand::Rng,
                 only_shrink: bool,
@@ -144,6 +159,17 @@
             ) -> anyhow::Result<Self::UserValue<'a>> {
                 Ok(*corpus_value)
             }
+
+            fn from_value(&self, value: Self::UserValue<'_>) -> anyhow::Result<Self::CorpusValue> {
+                Ok(value)
+            }
+
+            fn validate_corpus_value(
+                &self,
+                _corpus_value: &Self::CorpusValue,
+            ) -> anyhow::Result<()> {
+                Ok(())
+            }
         }
     };
 }
@@ -167,12 +193,12 @@
             type UserValue<'user> = $ty;
             type CorpusValue = $ty;
 
-            fn init(&self, rng: &mut dyn rand::Rng) -> anyhow::Result<Self::CorpusValue> {
+            fn init(&mut self, rng: &mut dyn rand::Rng) -> anyhow::Result<Self::CorpusValue> {
                 Ok(choose_value(rng))
             }
 
             fn mutate(
-                &self,
+                &mut self,
                 val: &mut Self::CorpusValue,
                 rng: &mut dyn rand::Rng,
                 only_shrink: bool,
@@ -215,6 +241,17 @@
             ) -> anyhow::Result<Self::UserValue<'a>> {
                 Ok(*corpus_value)
             }
+
+            fn from_value(&self, value: Self::UserValue<'_>) -> anyhow::Result<Self::CorpusValue> {
+                Ok(value)
+            }
+
+            fn validate_corpus_value(
+                &self,
+                _corpus_value: &Self::CorpusValue,
+            ) -> anyhow::Result<()> {
+                Ok(())
+            }
         }
     };
 }
@@ -263,12 +300,12 @@
     type UserValue<'user> = char;
     type CorpusValue = char;
 
-    fn init(&self, rng: &mut dyn rand::Rng) -> anyhow::Result<Self::CorpusValue> {
+    fn init(&mut self, rng: &mut dyn rand::Rng) -> anyhow::Result<Self::CorpusValue> {
         Ok(choose_value(rng))
     }
 
     fn mutate(
-        &self,
+        &mut self,
         val: &mut Self::CorpusValue,
         rng: &mut dyn rand::Rng,
         only_shrink: bool,
@@ -300,18 +337,26 @@
     ) -> anyhow::Result<Self::UserValue<'a>> {
         Ok(*corpus_value)
     }
+
+    fn from_value(&self, value: Self::UserValue<'_>) -> anyhow::Result<Self::CorpusValue> {
+        Ok(value)
+    }
+
+    fn validate_corpus_value(&self, _corpus_value: &Self::CorpusValue) -> anyhow::Result<()> {
+        Ok(())
+    }
 }
 
 impl Domain for Arbitrary<()> {
     type UserValue<'user> = ();
     type CorpusValue = ();
 
-    fn init(&self, _rng: &mut dyn rand::Rng) -> anyhow::Result<Self::CorpusValue> {
+    fn init(&mut self, _rng: &mut dyn rand::Rng) -> anyhow::Result<Self::CorpusValue> {
         Ok(())
     }
 
     fn mutate(
-        &self,
+        &mut self,
         _val: &mut Self::CorpusValue,
         _rng: &mut dyn rand::Rng,
         _only_shrink: bool,
@@ -326,6 +371,14 @@
     ) -> anyhow::Result<Self::UserValue<'a>> {
         Ok(())
     }
+
+    fn from_value(&self, value: Self::UserValue<'_>) -> anyhow::Result<Self::CorpusValue> {
+        Ok(value)
+    }
+
+    fn validate_corpus_value(&self, _corpus_value: &Self::CorpusValue) -> anyhow::Result<()> {
+        Ok(())
+    }
 }
 
 #[cfg(test)]
@@ -412,7 +465,7 @@
         CorpusValueForArbitrary<T>:
             std::fmt::Debug + Default + Clone + Copy + PartialOrd + PartialEq + 'static,
     {
-        let domain = Arbitrary::<T>::default();
+        let mut domain = Arbitrary::<T>::default();
         let mut rng = get_rng();
         let mut value = domain.init(&mut rng).unwrap();
 
@@ -442,7 +495,7 @@
             + std::hash::Hash
             + 'static,
     {
-        let domain = Arbitrary::<T>::default();
+        let mut domain = Arbitrary::<T>::default();
         let mut rng = get_rng();
         for _ in 0..100 {
             let mut value = domain.init(&mut rng).unwrap();
@@ -474,7 +527,7 @@
             + NumTraitsExtended
             + 'static,
     {
-        let domain = Arbitrary::<T>::default();
+        let mut domain = Arbitrary::<T>::default();
         let mut rng = get_rng();
 
         // Get a value that is not the shrink target
@@ -619,7 +672,7 @@
     }
 
     fn test_bool_shrink() {
-        let domain = Arbitrary::<bool>::default();
+        let mut domain = Arbitrary::<bool>::default();
         let mut rng = get_rng();
         let mut value = true;
         domain.mutate(&mut value, &mut rng, true).unwrap();
@@ -636,7 +689,7 @@
     #[test]
     fn test_unit() {
         let mut rng = get_rng();
-        let domain = Arbitrary::<()>::default();
+        let mut domain = Arbitrary::<()>::default();
 
         // init() always returns ()
         assert_eq!(domain.init(&mut rng).unwrap(), ());
@@ -659,7 +712,7 @@
             Float + SampleUniform + std::fmt::Display + std::fmt::Debug + SpecialValues + 'static,
         StandardUniform: Distribution<T>,
     {
-        let domain = Arbitrary::<T>::default();
+        let mut domain = Arbitrary::<T>::default();
         let mut rng = get_rng();
 
         // Positive.
@@ -723,7 +776,7 @@
 
     #[test]
     fn test_char_mutate_boundaries() {
-        let domain = Arbitrary::<char>::default();
+        let mut domain = Arbitrary::<char>::default();
         let mut rng = get_rng();
         let mut val = '\u{0000}';
         domain.mutate(&mut val, &mut rng, false).unwrap();
@@ -762,7 +815,7 @@
     #[test]
     fn test_char_shrink_to_null() {
         for _ in 0..10 {
-            let domain = Arbitrary::<char>::default();
+            let mut domain = Arbitrary::<char>::default();
             let mut rng = get_rng();
             let mut value = domain.init(&mut rng).unwrap();
 
diff --git a/rust/src/domains/containers.rs b/rust/src/domains/containers.rs
index a15e28c..7dee293 100644
--- a/rust/src/domains/containers.rs
+++ b/rust/src/domains/containers.rs
@@ -1,6 +1,5 @@
 use rand::RngExt;
 use std::fmt;
-
 use super::Domain;
 
 const DEFAULT_MAX_LEN: usize = 5000;
@@ -113,7 +112,12 @@
 
 impl<T> VecOf<T> {
     pub fn new(inner: T) -> Self {
-        Self { inner, min_len: 0, max_len: None, max_len_is_soft: false }
+        Self {
+            inner,
+            min_len: 0,
+            max_len: None,
+            max_len_is_soft: false,
+        }
     }
 
     fn max_len(&self) -> usize {
@@ -128,7 +132,7 @@
     type CorpusValue = Vec<T::CorpusValue>;
     type UserValue<'user> = Vec<T::UserValue<'user>>;
 
-    fn init(&self, rng: &mut dyn rand::Rng) -> anyhow::Result<Self::CorpusValue> {
+    fn init(&mut self, rng: &mut dyn rand::Rng) -> anyhow::Result<Self::CorpusValue> {
         if self.max_len() == 0 {
             return Ok(Vec::new());
         }
@@ -143,7 +147,7 @@
     }
 
     fn mutate(
-        &self,
+        &mut self,
         val: &mut Self::CorpusValue,
         rng: &mut dyn rand::Rng,
         only_shrink: bool,
@@ -193,9 +197,40 @@
         }
         Ok(user_values)
     }
+
+    fn from_value(&self, value: Self::UserValue<'_>) -> anyhow::Result<Self::CorpusValue> {
+        let mut corpus_values = Vec::with_capacity(value.len());
+        for item in value {
+            corpus_values.push(self.inner.from_value(item)?);
+        }
+        Ok(corpus_values)
+    }
+
+    fn validate_corpus_value(&self, corpus_value: &Self::CorpusValue) -> anyhow::Result<()> {
+        if self.max_len_is_soft {
+            anyhow::ensure!(
+                self.min_len <= corpus_value.len(),
+                "Length {} is less than the minimum length {}",
+                corpus_value.len(),
+                self.min_len
+            );
+        } else {
+            anyhow::ensure!(
+                self.min_len <= corpus_value.len() && corpus_value.len() <= self.max_len(),
+                "Length {} is not between the minimum length {} and maximum length {}",
+                corpus_value.len(),
+                self.min_len,
+                self.max_len()
+            );
+        }
+        for item in corpus_value {
+            self.inner.validate_corpus_value(item)?;
+        }
+        Ok(())
+    }
 }
 
-impl<T> ContainerDomain for VecOf<T> {
+impl<T: Domain> ContainerDomain for VecOf<T> {
     fn with_len(self, len: usize) -> Self {
         Self { min_len: len, max_len: Some(len), ..self }
     }
@@ -245,7 +280,7 @@
 
     #[gtest]
     fn test_vec_of_mutate_shrink() {
-        let domain = VecOf::new(Arbitrary::<u32>::default()).with_max_len(10);
+        let mut domain = VecOf::new(Arbitrary::<u32>::default()).with_max_len(10);
 
         let mut rng = get_rng();
 
@@ -264,7 +299,7 @@
 
     #[gtest]
     fn test_vec_of_mutate_grow_and_change() {
-        let domain = VecOf::new(Arbitrary::<u32>::default()).with_max_len(10);
+        let mut domain = VecOf::new(Arbitrary::<u32>::default()).with_max_len(10);
 
         let mut rng = get_rng();
 
@@ -284,7 +319,7 @@
 
     #[gtest]
     fn test_vec_of_init_respects_min_len() {
-        let domain = VecOf::new(Arbitrary::<u32>::default()).with_min_len(5);
+        let mut domain = VecOf::new(Arbitrary::<u32>::default()).with_min_len(5);
         let mut rng = get_rng();
 
         for _ in 0..100 {
@@ -295,7 +330,7 @@
 
     #[gtest]
     fn test_vec_of_init_fixed_len() {
-        let domain = VecOf::new(Arbitrary::<u32>::default()).with_len(7);
+        let mut domain = VecOf::new(Arbitrary::<u32>::default()).with_len(7);
         let mut rng = get_rng();
 
         for _ in 0..100 {
@@ -306,7 +341,7 @@
 
     #[gtest]
     fn test_vec_of_init_default_max_len() {
-        let domain = VecOf::new(Arbitrary::<u32>::default());
+        let mut domain = VecOf::new(Arbitrary::<u32>::default());
         let mut rng = get_rng();
 
         for _ in 0..100 {
@@ -317,7 +352,7 @@
 
     #[gtest]
     fn test_vec_of_mutate_respects_min_len() {
-        let domain = VecOf::new(Arbitrary::<u32>::default()).with_min_len(3);
+        let mut domain = VecOf::new(Arbitrary::<u32>::default()).with_min_len(3);
         let mut rng = get_rng();
 
         let mut val = vec![1, 2, 3];
@@ -329,7 +364,7 @@
 
     #[gtest]
     fn test_vec_of_mutate_respects_max_len() {
-        let domain = VecOf::new(Arbitrary::<u32>::default()).with_max_len(3);
+        let mut domain = VecOf::new(Arbitrary::<u32>::default()).with_max_len(3);
         let mut rng = get_rng();
 
         let mut val = vec![1, 2, 3];
@@ -341,7 +376,7 @@
 
     #[gtest]
     fn test_vec_of_mutate_min_len_validation() {
-        let domain = VecOf::new(Arbitrary::<u32>::default()).with_min_len(5);
+        let mut domain = VecOf::new(Arbitrary::<u32>::default()).with_min_len(5);
         let mut rng = get_rng();
 
         let mut val = vec![1, 2, 3]; // Length 3, which is < 5
@@ -358,7 +393,7 @@
 
     #[gtest]
     fn test_vec_of_mutate_soft_max_len_behavior() {
-        let domain = VecOf::new(Arbitrary::<u32>::default()).with_soft_max_len(5);
+        let mut domain = VecOf::new(Arbitrary::<u32>::default()).with_soft_max_len(5);
         let mut rng = get_rng();
 
         // Valid mutation within bounds
@@ -394,7 +429,7 @@
 
     #[gtest]
     fn test_vec_of_mutate_no_action_at_bounds() {
-        let domain = VecOf::new(Arbitrary::<u32>::default()).with_len(1);
+        let mut domain = VecOf::new(Arbitrary::<u32>::default()).with_len(1);
         let mut rng = get_rng();
 
         let mut val = vec![100u32];
@@ -415,7 +450,7 @@
 
     #[gtest]
     fn test_vec_of_zero_len() {
-        let domain = VecOf::new(Arbitrary::<u32>::default()).with_len(0);
+        let mut domain = VecOf::new(Arbitrary::<u32>::default()).with_len(0);
         let mut rng = get_rng();
 
         let val = domain.init(&mut rng).unwrap();
@@ -433,4 +468,5 @@
         let user_val = domain.get_user_value(&corpus_val).unwrap();
         expect_that!(user_val, container_eq(vec![1u32, 2u32, 3u32]));
     }
+
 }
diff --git a/rust/src/domains/range.rs b/rust/src/domains/range.rs
index 3bf413b..cb5f641 100644
--- a/rust/src/domains/range.rs
+++ b/rust/src/domains/range.rs
@@ -17,31 +17,32 @@
 use anyhow;
 use rand::distr::uniform::SampleUniform;
 use rand::distr::uniform::UniformSampler;
+use std::fmt;
 
 /// Generates values of type `T` in a given range.
-///
-/// For example, `InRange::new(0, 100)` generates integer
-/// values from the inclusive range `[0, 100]`.
-///
-/// Example usage:
-/// ```
-/// # use fuzztest::domains::Domain;
-/// # use fuzztest::domains::range::InRange;
-/// # use rand::prelude::*;
-///
-/// let range_i32 = InRange::new(21i32, 73);
-/// let sample = range_i32.init(&mut rand::rng());
-///
-/// assert!(sample.is_ok());
-/// let sample = sample.unwrap();
-/// assert!(sample >= 21);
-/// assert!(sample <= 73);
-/// ```
 pub struct InRange<T> {
     lower: T,
     upper: T,
 }
 
+impl<T: Clone> Clone for InRange<T> {
+    fn clone(&self) -> Self {
+        Self {
+            lower: self.lower.clone(),
+            upper: self.upper.clone(),
+        }
+    }
+}
+
+impl<T: fmt::Debug + Clone> fmt::Debug for InRange<T> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.debug_struct("InRange")
+            .field("lower", &self.lower)
+            .field("upper", &self.upper)
+            .finish()
+    }
+}
+
 impl InRange<i32> {
     pub fn new(lower: i32, upper: i32) -> Self {
         Self { lower, upper }
@@ -57,12 +58,12 @@
     type UserValue<'user> = i32;
     type CorpusValue = i32;
 
-    fn init(&self, rng: &mut dyn rand::Rng) -> anyhow::Result<Self::CorpusValue> {
+    fn init(&mut self, rng: &mut dyn rand::Rng) -> anyhow::Result<Self::CorpusValue> {
         Ok(self.get_in_range(rng))
     }
 
     fn mutate(
-        &self,
+        &mut self,
         val: &mut Self::CorpusValue,
         rng: &mut dyn rand::Rng,
         only_shrink: bool,
@@ -81,6 +82,20 @@
     ) -> anyhow::Result<Self::UserValue<'a>> {
         Ok(*corpus_value)
     }
+
+    fn from_value(&self, value: Self::UserValue<'_>) -> anyhow::Result<Self::CorpusValue> {
+        Ok(value)
+    }
+
+    fn validate_corpus_value(&self, corpus_value: &Self::CorpusValue) -> anyhow::Result<()> {
+        if *corpus_value < self.lower || *corpus_value > self.upper {
+            anyhow::bail!(
+                "Value {} is out of range [{}, {}]",
+                corpus_value, self.lower, self.upper
+            );
+        }
+        Ok(())
+    }
 }
 
 #[cfg(test)]
diff --git a/rust/src/internal.rs b/rust/src/internal.rs
index 213e66a..5f7e95b 100644
--- a/rust/src/internal.rs
+++ b/rust/src/internal.rs
@@ -14,8 +14,11 @@
 
 use super::domains::GenericCorpusValue;
 use super::domains::GenericDomain;
+
 use std::collections::HashMap;
+use std::sync::Arc;
 use std::sync::LazyLock;
+use std::sync::Mutex;
 
 /// A trait implemented by types used to Fuzz a given property function.
 ///
@@ -35,7 +38,7 @@
     /// Returns `true` if the property function holds, `false` if it crashes.
     fn execute(&self, args: &GenericCorpusValue) -> bool;
     fn print_finding_report(&self);
-    fn domains(&self) -> &dyn GenericDomain;
+    fn domains(&self) -> Arc<Mutex<dyn GenericDomain>>;
 }
 
 /// Identifies the property function of a fuzz test.
diff --git a/rust/src/worker.rs b/rust/src/worker.rs
index 8eae57f..d4786f9 100644
--- a/rust/src/worker.rs
+++ b/rust/src/worker.rs
@@ -158,7 +158,9 @@
     }
 
     pub fn get_random_seed_input(&self, sink: &mut InputSink) {
-        match self.fuzz_test.domains().init(&mut rand::rng()) {
+        let domains = self.fuzz_test.domains();
+        let mut domains_guard = domains.lock().expect("Failed to lock domains");
+        match domains_guard.init(&mut rand::rng()) {
             Ok(val) => {
                 sink.emit(pack_input(val));
             }
@@ -170,8 +172,10 @@
 
     pub fn mutate(&self, origin: &GenericCorpusValue, shrink: bool, sink: &mut InputSink) {
         let mut mutant = origin.clone();
+        let domains = self.fuzz_test.domains();
+        let mut domains_guard = domains.lock().expect("Failed to lock domains");
 
-        if let Err(e) = self.fuzz_test.domains().mutate(&mut mutant, &mut rand::rng(), shrink) {
+        if let Err(e) = domains_guard.mutate(&mut mutant, &mut rand::rng(), shrink) {
             emit_error(&format!("Failed to mutate: {:?}", e));
             return;
         }
@@ -211,7 +215,9 @@
     }
 
     pub fn serialize_input_content(&self, input: &GenericCorpusValue, sink: &mut BytesSink) {
-        match self.fuzz_test.domains().serialize_corpus(input) {
+        let domains = self.fuzz_test.domains();
+        let domains_guard = domains.lock().expect("Failed to lock domains");
+        match domains_guard.serialize_corpus(input) {
             Ok(serialized) => {
                 sink.emit(&serialized);
             }
@@ -222,7 +228,9 @@
     }
 
     pub fn deserialize_input_content(&self, content: &[u8], sink: &mut InputSink) {
-        match self.fuzz_test.domains().parse_corpus(content) {
+        let domains = self.fuzz_test.domains();
+        let domains_guard = domains.lock().expect("Failed to lock domains");
+        match domains_guard.parse_corpus(content) {
             Ok(val) => {
                 sink.emit(pack_input(val));
             }
@@ -639,6 +647,8 @@
 
     let mut generic_corpus_value = fuzztest
         .domains()
+        .lock()
+        .expect("Failed to lock domains")
         .init(&mut rng)
         .expect("domain initialization should succeed to provide an initial corpus value");
 
@@ -649,6 +659,8 @@
     while start_time.elapsed() < smoke_test_duration {
         fuzztest
             .domains()
+            .lock()
+            .expect("Failed to lock domains")
             .mutate(&mut generic_corpus_value, &mut rng, only_shrink)
             .expect("domain mutation should succeed");
         let result = fuzztest.execute(&generic_corpus_value);