Library / SDK
dtolnay/dyn-clone avatar
dtolnay/dyn-clone

dyn-clone: making Clone work inside a trait object

Clone trait that is object-safe

757 stars24 forksRustApache-2.0

At a glance

What is it?
dyn-clone is a small Rust crate that adds an object-safe cloning capability to trait objects, so a Box<dyn MyTrait> can be cloned and a struct containing one can derive Clone. It suits library authors who expose trait objects in their public APIs, and it is deliberately a small crate with a low MSRV and no runtime dependencies.
Who is it for?
dyn-clone fits a Rust library that hands callers a Box<dyn Trait> and needs that box to be cloneable, because it removes the boilerplate without adding a runtime dependency and keeps its MSRV at Rust 1.68. It does not fit code that needs more than cloning from a trait object, since equality, hashing, formatting and downcasting each need their own object-safe mechanism.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 33 days ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 25, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Why Clone does not work on a trait object

Rust's Clone trait has a size requirement. Cloning has to produce a value of the same type, so the trait needs Sized, and a trait with a Sized bound cannot be implemented for a trait object, since dyn Trait is by definition unsized.

The consequence shows up as soon as a public API hands out a Box<dyn Trait>. A caller who receives that box cannot clone it, even though every concrete type behind it is perfectly cloneable, and a struct that stores such a box cannot derive Clone.

dyn-clone is the fix for exactly that gap, and it is deliberately narrow. The crate describes itself as a Clone trait that is dyn-compatible, and the README says it provides a DynClone trait usable in trait objects plus a clone_box function that can clone any sized or dynamically sized implementation of DynClone. Types that implement the standard library's Clone trait are automatically usable by a DynClone trait object.

That last sentence is the design decision worth understanding. The crate does not ask you to write a clone implementation by hand for every type. It asks for a marker that says this trait can be cloned through a trait object, and it uses the standard Clone bound underneath, so an existing type that derives Clone needs nothing extra.

The DynClone bound and the clone_box entry point

The mechanism has two pieces. A trait called DynClone carries the object-safe clone operation, and a free function does the actual work. The README gives the signature of clone_box as:

rust
fn clone_box<T>(t: &T) -> Box<T>
where
    T: ?Sized + DynClone

The ?Sized on T is what makes this usable with trait objects, since the usual Sized bound is exactly what you are working around. A reference to a trait object can be passed in, and the result is a Box of the same dynamic type.

The flow in the README's example is straightforward. A trait is declared with DynClone as a supertrait, the trait is implemented for a concrete type, the concrete value is boxed as a Box<dyn MyTrait>, and then dyn_clone::clone_box is called on the dereferenced box. The type of the result is a Box<dyn MyTrait> cloned from the original, so it can be used through the same trait object interface.

For a library author, the practical consequence is that your trait must have DynClone as a supertrait, and every type that implements your trait must also implement Clone. The crate relies on that bound rather than providing an alternative path for types that are not Clone.

Deriving Clone on a struct that holds a boxed trait

Writing the Clone implementation for a boxed trait object by hand is the tedious part, and the crate ships a macro for it. The macro generates the implementation of Clone for Box<dyn MyTrait> in terms of dyn_clone::clone_box, which is what the README describes:

rust
// As before.
trait MyTrait: DynClone {
    /* ... */
}

dyn_clone::clone_trait_object!(MyTrait);

// Now data structures containing Box<dyn MyTrait> can derive Clone:
#[derive(Clone)]
struct Container {
    trait_object: Box<dyn MyTrait>,
}

The workflow for a first use is short. Add the dependency, add DynClone as a supertrait of the trait you want to expose as an object, invoke the macro once for that trait, and any struct holding a Box of that trait can now derive Clone.

Two things about the macro are worth knowing. It takes the trait name, so each exposed trait needs its own invocation, and it operates on the Box form specifically, which is why the generated impl is for Box<dyn MyTrait> rather than for the trait object alone. The dependency itself is declared in Cargo.toml under the package name dyn-clone, and the README's example file in the repository is examples/readme.rs, which mirrors the code shown in the README.

The macro covers one shape, so unusual cases still need code

The macro handles the common case, which is a Box of a trait object. Anything else is manual work.

If a type implements your trait but not Clone, the crate's approach does not apply, because DynClone relies on the standard Clone bound underneath. The README does not describe a fallback path for non-Clone types, and the fix in that case is to implement Clone for the concrete type or to use a different strategy for the object.

Similarly, the generated impl is for Box<dyn MyTrait>. A bare trait object reference, an Rc, an Arc or a struct that stores the trait object inline rather than boxed is not covered by the macro as documented, and each of those needs an implementation written out. That is a small amount of code, but it is code the crate does not remove.

The other limitation is about scope rather than mechanics. dyn-clone gives you cloning and nothing else. A trait object behind Box<dyn MyTrait> still cannot be compared for equality, hashed, formatted with Debug or downcast to a concrete type, because each of those needs its own object-safe operation with its own trait object type. If your API needs several of those at once, the design usually wants an enum of concrete types, or a wrapper type of your own, rather than a pile of helper traits.

Alternatives to a cloning helper trait

There are three other ways to solve the same problem, and the differences matter depending on who writes the code.

The first is writing the Clone implementation yourself. Instead of a macro, you write an impl Clone for Box<dyn MyTrait> that calls into a clone method on your trait. The difference is that the trait now needs an object-safe clone method of its own, which means every implementor provides it, even those that could have relied on the standard Clone trait. The crate's approach keeps that burden off implementors by reusing the standard bound.

The second is the type erasure approach: erase the concrete type to a form that can be recovered by downcasting, clone it once the concrete type is known, and re-erase it. The difference in requirements is real, since that approach needs the types to be 'static and carry an identity type, while dyn-clone needs nothing beyond Clone.

The third is to stop using trait objects for the field at all. An enum with one variant per concrete type can derive Clone directly, and it can also derive Debug and PartialEq for free. The cost is that adding a new implementation means editing the enum, so the openness that trait objects buy you is gone. For a library whose extension point is meant to be open, that trade-off usually goes the wrong way, which is why a small crate like this exists.

A dependency with no runtime weight

dyn-clone is a small crate by design. The Cargo.toml lists no runtime dependencies at all, with the dev-dependencies limited to rustversion and trybuild, the latter with the diff feature. Trybuild is the standard way to test that a piece of code fails to compile in the way you expect, which fits a crate whose whole purpose is to make certain code compile.

The package metadata also lists the categories rust-patterns and no-std, so the crate is meant to be usable without the standard library's allocation facilities. The edition is 2021 and the rust-version field says 1.68, which is a low bar by current Rust standards and matters for libraries that want a wide MSRV range.

The documentation is published to docs.rs, and the package metadata configures that build to target x86_64-unknown-linux-gnu with extra rustdoc arguments for generating links to definitions and macro expansions. There is a CI workflow on the master branch.

On the licence there is a small discrepancy worth knowing. The repository's licence field lists Apache-2.0, while the README says the crate is licensed under either Apache License 2.0 or the MIT license at your option, and the Cargo.toml records MIT OR Apache-2.0. The dual licence is the statement that appears in the crate's own documentation, and it is the one that governs how you can use it.

Release state and what a dependency upgrade costs

The crate sits at version 1.0.20 in Cargo.toml, and the published release list shows 1.0.20 on 2025-07-27, 1.0.19 on 2025-03-03 and 1.0.18 on 2025-02-01. The repository was last pushed on 2026-09-22, and it is not archived.

The default branch is master, and the releases are numbered, which is what you expect from a crate whose version is part of other people's dependency graph. The cost of a version bump is therefore the normal cost of any Rust dependency: read the changelog, and check whether the macro still applies to your trait definitions.

The trust question here is a different one from most dependencies. Since the crate has no runtime dependencies, the thing you are really trusting is the shape of the API, which is three names: the DynClone trait, the clone_box function and the clone_trait_object macro. That surface is small enough to read in full, which is the main argument for choosing this crate over a heavier general purpose dynamic trait library.

Editorial conclusion

dyn-clone fits a Rust library that hands callers a Box<dyn Trait> and needs that box to be cloneable, because it removes the boilerplate without adding a runtime dependency and keeps its MSRV at Rust 1.68. It does not fit code that needs more than cloning from a trait object, since equality, hashing, formatting and downcasting each need their own object-safe mechanism. Before you adopt it, run cargo test in your crate against the Rust version you ship, and check that every concrete type you intend to put behind the trait object implements std::clone::Clone, because the crate relies on that bound rather than providing a fallback.

Frequently asked questions

What does the dyn-clone crate provide?

It provides a DynClone trait that can be used in trait objects and a clone_box function that clones any sized or dynamically sized implementation of DynClone. Types implementing the standard library's Clone trait are automatically usable by a DynClone trait object.

How do I make a struct containing Box<dyn MyTrait> cloneable in Rust?

Declare your trait with DynClone as a supertrait, then invoke `dyn_clone::clone_trait_object!(MyTrait)`. The macro generates the Clone implementation for Box<dyn MyTrait> in terms of dyn_clone::clone_box, so a struct holding that box can derive Clone.

What Rust version does dyn-clone require?

The package metadata sets rust-version to 1.68 and the edition to 2021. The crate has no runtime dependencies and is categorised as no-std as well as rust-patterns.

Can dyn-clone compare or downcast a trait object?

No. The crate only provides cloning. Equality, hashing, Debug formatting and downcasting each require their own object-safe mechanism, so a design that needs several of them is usually better served by an enum of concrete types.

Official sources

  1. dtolnay/dyn-clone on GitHub
  2. Issues
  3. License: Apache-2.0
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/dtolnay-dyn-clone.svg)](https://hysenlabs.com/projects/dtolnay-dyn-clone)

Was this useful?