rust-lang / rust-lang/rust-clippy
Detect trait object parameters being passed smart pointers which meet the trait object bounds.
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 13.5k
- Forks
- 2.2k
- Avg merge
- 2d 10h
- Merged PRs (30d)
- 32
Description
What it does
Detect functions which take trait objects and warn if a smart pointer is used as an argument to the function and the smart pointer matches the trait bounds of the trait object.
When implementing internal dynamic dispatch in an API, it may be desirable to have implementations defined using a trait with associated types. However, associated types make it impossible to create a trait object. One solution seen around ecosystem is to blanket implement a "dynamic" trait for all implementations of the other trait. For example:
trait Context: Send + Sync {
type Value: Send + 'static; // This associated type means Context cannot be a trait object
fn do_thing(&self, value: &Self::Value);
}
// This trait can be made into a trait object. However the values must be made into a trait object.
trait DynamicContext {
fn do_thing(&self, value: &(dyn Any + Send));
}
// And here is the blanket implementation.
impl<T> DynamicContext for T
where
T: Context + 'static,
{
fn do_thing(&self, value: &(dyn Any + Send)) {
T::do_thing(value.downcast_ref().unwrap())
}
}
A public type then may exist to expose the functionality in a type safe way for API users while allowing the implementation to select dynamically how the type should be implemented at runtime:
pub struct Thingy {
context: Arc<dyn DynamicContext>,
value: Box<dyn Any + Send>,
}
impl Thingy {
pub fn do_thing(&self) {
self.context.do_thing(&self.value)
}
}
However there is a problem when this code is run. We panic inside of the blanket implementation for do_thing.
This is because a Box<dyn Any + Send> can be upcast to a &(dyn Any + Send). What we actually wanted was the trait object of the inner value of the Box. But instead we got a Box<dyn Any + Send> upcast to the trait object. This can be fixed using as_ref in the case of a Box to access the inner value.
impl Thingy {
pub fn do_thing(&self) {
self.context.do_thing(self.value.as_ref())
}
}
This lint would could also be extended to other smart pointer types.
Lint Name
trait_object_inner_value
Category
suspicious
Advantage
- Code may assume the trait object passed in can be downcast to a concrete type. This would ensure smart pointers which fail to downcast correctly are warned about when passed as arguments.
Drawbacks
- Some code may assume that the trait object being upcast should be a
Box. - Smart pointer types defined outside of
stdwould not work under this lint. It might be possible to detect types which downcast to a trait object but have an inner value that is accessible somehow (via Deref for example) albeit with slightly worse diagnostics. This would not fully work withMutexGuard-like objects though.
Example
struct Thing;
fn downcast_thing(thing: &(dyn Any + Send)) {
thing.downcast_ref::<Thing>().unwrap()
}
fn with_box(thing_boxed: &Box<dyn Any + Send>) {
downcast_thing(thing_boxed)
}
Could be written as:
struct Thing;
fn downcast_thing(thing: &(dyn Any + Send)) {
thing.downcast_ref::<Thing>().unwrap()
}
fn with_box(thing_boxed: &Box<dyn Any + Send>) {
downcast_thing(thing_boxed.as_ref())
}
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the issue's Rust examples and the proposed lint name, trait_object_inner_value. Define the supported smart-pointer cases from the Box example and its as_ref correction, then verify that the lint warns on the incorrect argument and accepts the corrected form. No source files or tests are named in the issue, so locating the relevant Clippy lint and test entry points is part of the work.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- devtools
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100