1. Symptoms
When the Rust compiler encounters error E0161, you will see output similar to the following in your terminal or IDE:
error[E0161]: the trait bound `Drop + Sized` is not satisfied
--> src/main.rs:10:5
|
10 | fn drop(&mut self) { }
| ^^^^^^ unsized types have no automatic `Drop` implementations
|
= note: unsized types have no automatic `Drop` implementations
= note: this error occurs because the type has a `Drop` implementation in the
standard library, but not all its fields implement `Sized`
= help: consider moving the `Drop` implementation to a wrapper type
The error message indicates that you are attempting to use a feature that requires the Sized bound, but the type in question is unsized. Unsized types in Rust include trait objects (dyn Trait), slices ([T]), and the string slice type (str). These types do not have a known size at compile time, which prevents certain operations that require knowing the exact memory layout of a type.
You may encounter this error in several scenarios: implementing the Drop trait for a struct containing an unsized field, attempting to call sized-only methods on trait objects, or working with generic functions that implicitly require Sized. The compiler explicitly states that unsized types have no automatic Drop implementations, which is the core restriction causing this error.
2. Root Cause
The fundamental issue behind E0161 stems from Rust’s treatment of sized and unsized types. Every type in Rust either implements the Sized trait implicitly or is considered unsized. A type is Sized if the compiler knows its size at compile time, which is required for stack allocation, method dispatch, and automatic Drop implementations. Trait objects like dyn Trait, slices like [T], and str are inherently unsized because their size depends on data provided at runtime.
The Drop trait in Rust has an implicit Sized bound that is hidden from the source code but enforced by the compiler. This means that when you write a Drop implementation, you are implicitly promising that your type is Sized. If you attempt to implement Drop for a type that contains an unsized field, the compiler rejects the code because the unsized field breaks the Sized guarantee.
When working with generic code, functions that do not explicitly specify a ?Sized bound implicitly require Sized. For example, a function signature like fn process<T>(value: T) desugars to fn process<T: Sized>(value: T). Calling such a function with an unsized argument, such as a trait object or slice, triggers error E0161 because the type does not satisfy the implicit Sized bound.
Trait objects present a particularly common source of this error because they are intrinsically unsized. When you store a Box<dyn SomeTrait> in a struct or pass it to a generic function, the Box itself is sized, but the inner dyn SomeTrait is not. This distinction matters when implementing traits that carry the Sized requirement.
3. Step-by-Step Fix
There are several strategies to resolve E0161, depending on your specific use case. The key is understanding whether you need to work around the unsized nature of a type or explicitly accept unsized types in your generic code.
Solution 1: Implement Drop for a Sized Wrapper Type
If you need custom cleanup logic for a struct containing an unsized field, create a separate wrapper type that is fully sized:
Before:
struct Container {
data: [u8], // This makes Container unsized
}
impl Drop for Container {
fn drop(&mut self) {
println!("Cleaning up container");
}
}
After:
struct Container {
data: Vec<u8>, // Vec<u8> is Sized
}
impl Drop for Container {
fn drop(&mut self) {
println!("Cleaning up container");
}
}
Solution 2: Explicitly Accept Unsized Types with ?Sized
When writing generic functions, add the ?Sized bound to accept unsized arguments:
Before:
fn process<T>(value: T) {
// T implicitly requires Sized
}
After:
fn process<T: ?Sized>(value: &T) {
// Now accepts both Sized and unsized types
}
Solution 3: Use References for Trait Objects
When you need to implement a trait for a type that will be used as a trait object, work with references:
Before:
trait Printable {
fn print(&self);
}
struct MyStruct {
data: Box<dyn std::fmt::Display>,
}
impl Drop for MyStruct {
fn drop(&mut self) {
self.data.print();
}
}
After:
trait Printable {
fn print(&self);
}
struct MyStruct {
data: Vec<Box<dyn std::fmt::Display>>,
}
impl Drop for MyStruct {
fn drop(&mut self) {
for item in &self.data {
item.print();
}
}
}
Solution 4: Restructure Data to Be Sized
Instead of holding unsized data directly, use an indirection that is sized:
Before:
struct Document<'a> {
content: &'a str, // &str is sized, but the struct lifetime complicates things
}
After:
struct Document {
content: String, // String is fully owned and Sized
}
4. Verification
After applying your chosen fix, verify that the error is resolved by running the Rust compiler:
cargo build
A successful build produces output indicating compilation completed without errors. For more thorough verification, run your test suite:
cargo test
Ensure that the functionality you were implementing continues to work correctly. If you changed the data structure, confirm that all usages of the type throughout your codebase are updated accordingly. The compiler will catch any remaining type mismatches if you accidentally changed the public interface of a type.
For trait object scenarios specifically, write a test that constructs your type with various implementations of the relevant traits and verify that destruction occurs correctly:
#[test]
fn test_drop_called() {
use std::sync::atomic::{AtomicBool, Ordering};
static DROPPED: AtomicBool = AtomicBool::new(false);
struct DropTracker;
impl Drop for DropTracker {
fn drop(&mut self) {
DROPPED.store(true, Ordering::SeqCst);
}
}
struct Wrapper {
tracker: DropTracker,
trait_object: Box<dyn std::fmt::Display>,
}
let w = Wrapper {
tracker: DropTracker,
trait_object: Box::new(42),
};
drop(w);
assert!(DROPPED.load(Ordering::SeqCst), "Drop should have been called");
}
5. Common Pitfalls
One of the most frequent mistakes developers make when encountering E0161 is attempting to implement Drop directly on a struct that contains a trait object field. The implicit Sized bound on Drop makes this impossible without restructuring. Developers often overlook that Box<dyn Trait> is sized even though dyn Trait is not; the problem arises only when trying to make the containing struct itself unsized.
Another common pitfall involves forgetting the ?Sized bound on generic parameters. When writing library code that needs to work with both sized and unsized types, you must explicitly opt into unsized support. Many developers assume that all types work with generic functions, not realizing that the implicit Sized bound excludes slices and trait objects.
A subtle issue occurs when using associated types in traits. If a trait has an associated type that could be unsized, implementing that trait requires careful consideration of the Sized bounds on the associated type. The compiler will reject implementations where the associated type does not satisfy the trait’s requirements.
When working with custom allocators or custom smart pointers, ensure that your types properly implement Sized where needed. Custom pointer types sometimes inadvertently create unsized types if they wrap unsized data without providing a stable size guarantee.
Finally, be cautious when using Rc< dyn Trait > versus Arc< dyn Trait >. While both are sized containers, the mental model often confuses the sized wrapper with the unsized interior. The error E0161 specifically targets the unsized interior, not the smart pointer wrapper itself.
6. Related Errors
E0277: the trait bound was not satisfied
This error frequently accompanies E0161 because both stem from trait bounds not being satisfied. E0277 provides more detailed information about which specific bound is missing, while E0161 specifically highlights the Sized constraint. When you see both errors in sequence, address E0161 first by adding ?Sized where needed or restructuring your types.
E0038: trait objects cannot be used as associated constants
This error occurs when attempting to use trait objects in contexts that only support sized types, such as associated constants. While the error message differs from E0161, the underlying cause is the same: attempting to use an unsized type where a sized type is required. The fix typically involves either switching to a sized type or redesigning the abstraction to work with trait objects.
E0519: the parameter ... is not constrained by this impl trait, fn, or closure
This error sometimes appears alongside E0161 when working with impl Trait in return position or generic parameters. It indicates that the compiler cannot determine the concrete type needed for compilation. When combined with E0161, it suggests you are trying to return or pass an unsized type through a path that requires sized types, necessitating a redesign to use references, boxes, or explicit type constraints.