1 unstable release
0.1.0 | Aug 3, 2020 |
---|
#2201 in Development tools
9KB
91 lines
must-implement-trait
An attribute proc-macro which enforces that a type (auto-)implements the specified trait(s).
Usage
Apply the must_implement_trait
attribute to your struct or enum:
use must_implement_trait::must_implement_trait;
#[must_implement_trait(Send)]
struct Resources {
data: Rc<String>,
}
#[must_implement_trait(Send, UnwindSafe)]
enum ConfigSource {
File(String),
Database(DatabaseAdapter)
}
The attribute can take one or many trait names as parameters; it will enforce that all are implemented.
Motivation
Some traits, chiefly Send
and Sync
, are auto traits.
This means that they are automatically implemented by the compiler, and rarely implemented
(or un-implemented) by hand. Their implementations are implicit, and non-obvious to the reader
without looking at documentation.
As a library author, you may want to guarantee to consumers that your exposed interface will carry these traits. It may also be helpful within one's own code to provoke an error near the source of the un-implementation rather than near a consumer which fails to compile due to a trait bound violation.
Consider the following example (examples/async-future-send.rs
):
struct Resources {
data: Rc<String>,
}
struct MyResourceManager {}
#[async_trait]
impl ResourceManager for MyResourceManager {
async fn update_resources(resources: &Resources) {
// Some code to update resources...
}
}
We get an error within the update_resources
function telling us:
error: future cannot be sent between threads safely
--> examples/async-future-send.rs:21:54
|
21 | async fn update_resources(resources: &Resources) {
| ______________________________________________________^
22 | | // Some code to update resources...
23 | | }
| |_____^ future returned by `__update_resources` is not `Send`
|
= help: within `Resources`, the trait `std::marker::Sync` is not implemented for `std::rc::Rc<std::string::String>`
By default, async-trait
requires returned Futures to be Send
. That is a rather natural choice,
since async
is often used in multi-threaded contexts. In this case, since we used an Rc
rather
than an Arc
, Resources
is not Send; we are given an error at the usage site. If Resources
were in its own crate which is intended to be used in async contexts, the author may not realize
they have made a breaking change by introducing an Rc
.
In my experience, if working in a codebase which is async-oriented, it makes sense to ensure that
most or all types are Send + Sync
; it is easy to break downstream code if an auto-implementation
changes implicitly.
By applying must_implement_trait
, the error is moved to the type declaration:
error[E0277]: `std::rc::Rc<std::string::String>` cannot be sent between threads safely
--> examples/async-future-send.rs:11:1
|
11 | #[must_implement_trait(Send)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `std::rc::Rc<std::string::String>` cannot be sent between threads safely
|
= help: within `Resources`, the trait `std::marker::Send` is not implemented for `std::rc::Rc<std::string::String>`
= note: required because it appears within the type `Resources`
= help: see issue #48214
= note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)
Dependencies
~1.5MB
~37K SLoC