I am implementing a search algorithm library with two traits: SearchSpace and SearchAlgorithm.
I want to be able to "spy" some extra info in the search algorithm when running benchmarks, so I created a wrapper for a SearchSpace, a SpiedSearchSpace, to collect the data.
Finally, I want that any search algorithm that implements SearchAlgorithm<S> also implements SearchAlgorithm<SpiedSearchSpace<S>
Here is the simplified code:
trait SearchSpace {}
struct ASearchSpace;
impl SearchSpace for ASearchSpace {}
struct SpiedSearchSpace<S: SearchSpace>(S);
impl<S: SearchSpace> SearchSpace for SpiedSearchSpace<S> {}
trait SearchAlgorithm<S: SearchSpace> {}
// A base implementation for a search algorithm.
struct ASearchAlgorithm<S: SearchSpace>(S);
impl<S: SearchSpace> SearchAlgorithm<S> for ASearchAlgorithm<S> {}
// A recursive implementation, if something implements SearchAlgorithm<S> it should implement SearchAlgorithm<SpiedSearchSpace<S>> too.
impl<S: SearchSpace, A: SearchAlgorithm<S>> SearchAlgorithm<SpiedSearchSpace<S>> for A {}
In the playground
But the compiler complains about a conflicting implementation for the trait SearchAlgorithm:
error[E0119]: conflicting implementations of trait `SearchAlgorithm<SpiedSearchSpace<_>>` for type `ASearchAlgorithm<SpiedSearchSpace<_>>`
--> traits.rs:14:1
|
12 | impl<S: SearchSpace> SearchAlgorithm<S> for ASearchAlgorithm<S> {}
| --------------------------------------------------------------- first implementation here
13 |
14 | impl<S: SearchSpace, A: SearchAlgorithm<S>> SearchAlgorithm<SpiedSearchSpace<S>> for A {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `ASearchAlgorithm<SpiedSearchSpace<_>>`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0119`.
As I understand this is a recursive trait implementation, given a manual implementation
impl SearchAlgorithm<S> for ASearchAlgorithm<S> { ... }
ASearchAlgorithm<S> would also implement SearchAlgorithm<SpiedSearchSpace<S>> and SearchAlgorithm<SpiedSearchSpace<SpiedSearchSpace<S>>> and so on.
But I can't see how two conflicting implementations of SearchAlgorithm<SpiedSearchSpace<S>> could arise.
Could someone help me understand where and how the conflict arises?
Related
Reproduction project (single main.rs file): https://github.com/frederikhors/iss-custom-err.
I'm trying to create a custom error for my app:
pub struct AppError {
message: String,
error: anyhow::Error, // In the future I would also avoid anyhow
}
I'm trying to use it in my code but as you can see I'm getting the below compiler errors, why?
Isn't my AppError implementing the trait std::error::Error correctly?
I would expect an auto conversion from hyper error to AppError being both error:Error traits, am I wrong?
error[E0277]: `?` couldn't convert the error to `AppError`
--> src\main.rs:20:44
|
20 | .body(Body::from(body))?;
| ^ the trait `From<hyper::http::Error>` is not implemented for `AppError`
|
= note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
= help: the following other types implement trait `FromResidual<R>`:
<Result<T, F> as FromResidual<Result<Infallible, E>>>
<Result<T, F> as FromResidual<Yeet<E>>>
= note: required because of the requirements on the impl of `FromResidual<Result<Infallible, hyper::http::Error>>` for `Result<(), AppError>`
error[E0277]: `?` couldn't convert the error to `AppError`
--> src\main.rs:24:19
|
24 | .await?;
| ^ the trait `From<hyper::Error>` is not implemented for `AppError`
|
= note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
= help: the following other types implement trait `FromResidual<R>`:
<Result<T, F> as FromResidual<Result<Infallible, E>>>
<Result<T, F> as FromResidual<Yeet<E>>>
= note: required because of the requirements on the impl of `FromResidual<Result<Infallible, hyper::Error>>` for `Result<(), AppError>`
For more information about this error, try `rustc --explain E0277`.
I would expect an auto conversion from hyper error to AppError being both error:Error traits, am I wrong?
Yes. Error is a usage trait, it's like IntoIterator: both Vec and HashMap implement it, but that doesn't mean you can convert an arbitrary Vec to a HashMap.
Let alone that Rust will do it for you: Rust generally favors intentionality, it avoids large multi-use concepts. So all error tells you is that you can display the type, and may be able to get its source. It says nothing about conversion, except for conversion to a Box<dyn Error> (because Error is object-safe).
So as the compiler error tells you, if you want Rust (and specifically ?) to perform the conversion, you need to implement the From trait.
Note: libraries like thiserror or snafu provide tooling to more easily create bespoke error types, and conversions from existing error types.
On discord a kind user helped me understand.
What I need is a generic impl:
impl<T: error::Error + Send + Sync + 'static> From<T> for AppError {
fn from(e: T) -> Self {
Self { message: e.to_string(), error: anyhow::Error::new(e) }
}
}
That's it!
In the code below: Rust Playground
//////////////////////////////////////
// External code from another crate //
//////////////////////////////////////
trait FooExternal {
fn foo(&self);
}
fn foo_external(f: impl FooExternal) {
f.foo();
}
/////////////////////////////////////
// My generated library code below //
/////////////////////////////////////
trait FooImpl {
fn foo_impl(&self);
}
impl<T: FooImpl> FooExternal for T {
fn foo(&self) {
println!("foo: boilerplate");
self.foo_impl();
}
}
////////////////////////
// My user code below //
////////////////////////
#[derive(Debug, Default)]
struct Foo;
// NB: the compiler will yell if FooImpl is not implemented
// try commenting out the impl below
impl FooImpl for Foo {
fn foo_impl(&self) {
println!("foo_impl");
}
}
fn main() {
println!("Hello, world!");
let f = Foo::default();
foo_external(f);
}
The external code expects user to supply a type that implements FooExternal, so foo_external can work on an instance of the type.
However, when implementing the FooExternal trait for a user defined struct Foo, there is some boilerplate code (e.g. setting up opentelemetry tracing span context for each tonic grpc handler) that is common and error prone and a little complex to require an end user to type out.
So I plan to generate (think: proc macro, but codegen is not the issue here!) the common boilerplate implementation of FooExternal, and want user to only focus on the core app logic, and not worrying about the complex and boring chore of typing the same boilerplate over and over again!
So, instead of having user to implement FooExternal for her type Foo, I want the user to implement a generated trait FooImpl, where in the generated blanket trait implementation of FooExternal::foo, the boilerplate code is emitted, and the control is forwarded to FooImpl::foo_impl.
Here's the nice thing in the user code: FooImpl becomes a requirement for the user type Foo (when using foo_external) - if the user forgets to implement FooImpl for Foo, the compiler would kindly yell at you when calling foo_external(f)!
So it seems that the trait blanket implementation effectively have FooExternal bound on FooImpl - even the compiler error message (when the impl FooImpl for Foo is commented out) says:
Compiling playground v0.0.1 (/playground)
error[E0277]: the trait bound `Foo: FooImpl` is not satisfied
--> src/main.rs:49:18
|
49 | foo_external(f);
| ^ the trait `FooImpl` is not implemented for `Foo`
|
note: required because of the requirements on the impl of `FooExternal` for `Foo`
--> src/main.rs:23:18
|
23 | impl<T: FooImpl> FooExternal for T {
| ^^^^^^^^^^^ ^
note: required by a bound in `foo_external`
--> src/main.rs:11:25
|
11 | fn foo_external(f: impl FooExternal) {
| ^^^^^^^^^^^ required by this bound in `foo_external`
For more information about this error, try `rustc --explain E0277`.
error: could not compile `playground` due to previous error
So, I'm wondering that is trait blanket implementation ever designed to be used in such a scenario (basically tie FooImpl and FooExternal together, a little like extension trait trait FooExternal: FooImpl, but not quite!), and can I rely on this behavior to define my code generation logic to generate the library code as in the code sample?
Any insights would be greatly appreciated!
According to the trait coherence rules, impl<T: FooImpl> FooExternal for T must be in the same crate as FooExternal. So, if you do not control the crate FooExternal is defined in, you cannot do this.
The rules are are designed so that adding more crates can never create a conflict between trait implementations that wasn't there already. There can only be one blanket implementation impl<T...> FooExternal for T (because if there was more than one, they might both apply to the same type T), and only the crate defining FooExternal is allowed to write that implementation.
I'm creating a macro that parses a user-generated trait and creates helper functions for it. I have type restrictions on some of the arguments used, so I've been following the example in the quote crate to generate some code that'll throw a compilation error if a trait isn't implemented. It's not perfect but it gets the job done:
struct _AssertSync where #ty: Sync;
I'd also like to be able to check if the trait has Hash as a supertrait. If I follow the same pattern, I get an error:
struct _AssertHash where dyn #tr: Hash;
A complete example:
use std::hash::Hash;
trait BadBoi {}
trait GoodBoi: Hash {}
struct _AssertHash
where
dyn GoodBoi: Hash;
error[E0038]: the trait `GoodBoi` cannot be made into an object
--> src/lib.rs:6:39
|
4 | trait GoodBoi: Hash {}
| ------- this trait cannot be made into an object...
5 |
6 | struct _AssertHash where dyn GoodBoi: Hash;
| ^^^^ the trait `GoodBoi` cannot be made into an object
|
= help: consider moving `hash` to another trait
Is there any way for me to get around this?
You can check a supertrait-relationship by using two functions, for example:
fn _assert_hash_supertrait<T: $tr>() {
fn requires_hash<T: Hash>() {}
let _ = requires_hash::<T>;
}
This only compiles if the bound T: $tr implies T: Hash. And the only way that's the case if Hash is a super-trait of $tr. Also see this helpful Q&A.
Full example (Playground):
trait BadBoi {}
trait GoodBoi: Hash {}
macro_rules! foo {
($tr:ident) => {
fn _assert_hash_supertrait<T: $tr>() {
fn requires_hash<T: Hash>() {}
let _ = requires_hash::<T>;
}
}
}
//foo!(GoodBoi);
foo!(BadBoi);
Of course in this solution, calling foo! twice errors because assert_hash_supertrait is defined twice, but solving that is not part of this question.
I have created a trait for transforming from some values to a type I need. That conversion is already covered by From/Into for many types, but not everything I want. I thought I could exploit this, but quickly got an error "upstream crates may add a new impl of trait".
(stripped-down example in the playground)
pub trait Cookable {
fn cook(self) -> (String, Vec<i8>);
}
impl<T: Into<Vec<i8>>> Cookable for T {
fn cook(self) -> (String, Vec<i8>) {
(String::from("simple"), self.into())
}
}
impl Cookable for &str {
fn cook(self) -> (String, Vec<i8>) {
(String::from("smelly"), vec![self.len()])
}
}
That triggers the following error:
error[E0119]: conflicting implementations of trait `Cookable` for type `&str`:
--> src/lib.rs:11:1
|
5 | impl<T: Into<Vec<i8>>> Cookable for T {
| ------------------------------------- first implementation here
...
11 | impl Cookable for &str {
| ^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `&str`
|
= note: upstream crates may add a new impl of trait `std::convert::From<&str>` for type `std::vec::Vec<i8>` in future versions
I am worried that the only way to work around this error is to specify individual trait implementations for every one of the types that already has an Into.
It's not a problem you can "work around". It's a limitation imposed by the compiler to prevent future changes to your dependencies from subtly changing the behavior of your code.
For now, you avoid the error by implementing for concrete types instead of using generics and traits. A macro is one way to reduce the amount of keyboard entry you have to do.
In the future, some form of specialization might also be useful to solve this. However, this sits squarely in the middle of the reason that specialization isn't stable. It's possible to use this type of specialization to create unsound Rust using only safe code. A reduced form of specialization is being worked on, but it deliberately eschews the ability to specialize based on a trait and only works for concrete types.
See also:
Resolving trait implementation conflicts
Conflicting implementations of trait in Rust
How can I implement From for both concrete Error types and Box<Error> in Rust?
How is there a conflicting implementation of `From` when using a generic type?
I implemented a trait for another trait but cannot call methods from both traits
Based on the answers to the questions listed by Shepmaster, I have come up with the following workaround which appears to accomplish my mission. It is an implementation of the "use a macro to compactly do all the boilerplate of implementing the trait for a bunch of structs":
extern crate jni;
use jni::objects::{JObject, JValue};
use jni::JNIEnv;
pub trait ConvertRustToJValue<'a> {
fn into_jvalue(self, je: &JNIEnv<'a>) -> JValue<'a>;
}
macro_rules! impl_convert_rust_to_jvalue {
( $($t:ty),* ) => {
$( impl<'a> ConvertRustToJValue<'a> for $t
{
fn into_jvalue(self, _je:&JNIEnv) -> JValue<'a>
{
self.into()
}
}) *
}
}
impl_convert_rust_to_jvalue! { i8, i16, i32, i64 }
I am curious to see how much boilerplate one can save through built-in reflection.
A little background
My idea behind structured logging is to use various small tailored types to separate content from representation. Instead of unstructured logger.info("Found a bar with {} foos", bar.foo) one uses something like logger.info(FoundBar{ _bar: bar })
My Rust-ish approach
define a Log trait
provide a default implementation that calls the Serde machinery to serialize the type (to JSON in this example)
define loggable types easily by letting them "inherit" the default implementation
profit
Define the trait, providing a default impl:
trait Log {
fn to_log(&self) -> String {
serde_json::to_string(&self).unwrap()
}
}
(RLS is already drawing angry red squiggles, but bear with me)
Define a simple type to be logged:
#[derive(Serialize)]
struct Message {
msg: String,
}
and let it use the default implementation:
impl Log for Message {}
and finally the polymorphic logging function defined in terms of the trait:
fn log(log: &Log) {
println!("serialized = {}", log.to_log());
}
The compiler complains:
error[E0277]: the trait bound `Self: _IMPL_DESERIALIZE_FOR_Message::_serde::Serialize` is not satisfied
--> src\main.rs:8:9
|
8 | serde_json::to_string(&self).unwrap()
| ^^^^^^^^^^^^^^^^^^^^^ the trait `_IMPL_DESERIALIZE_FOR_Message::_serde::Serialize` is not implemented for `Self`
|
= help: consider adding a `where Self: _IMPL_DESERIALIZE_FOR_Message::_serde::Serialize` bound
= note: required because of the requirements on the impl of `_IMPL_DESERIALIZE_FOR_Message::_serde::Serialize` for `&Self`
= note: required by `serde_json::ser::to_string`
Adding the where Self suggestion to my trait function only produces different errors (error[E0433]: failed to resolve. Use of undeclared type or module _IMPL_DESERIALIZE_FOR_Message), but apart from that it seems like a Bad Idea(TM) to have this implementation detail of Serde leak into my code.
How do I portably constrain my trait (using where?) to only apply to types that have the correct derive? Even better, can I "inject" the derive functionality into types using the trait?
If you create a MCVE of your problem on the playground, you get a more accurate error:
error[E0277]: the trait bound `Self: serde::Serialize` is not satisfied
--> src/lib.rs:6:9
|
6 | serde_json::to_string(&self).unwrap()
| ^^^^^^^^^^^^^^^^^^^^^ the trait `serde::Serialize` is not implemented for `Self`
|
= help: consider adding a `where Self: serde::Serialize` bound
= note: required because of the requirements on the impl of `serde::Serialize` for `&Self`
= note: required by `serde_json::ser::to_string`
Following the suggestion, but using the idiomatic supertrait syntax, answers your question:
trait Log: serde::Serialize {
fn to_log(&self) -> String {
serde_json::to_string(&self).unwrap()
}
}
You'll need to change your log function for object-safety reasons:
fn log(log: &impl Log) {
println!("serialized = {}", log.to_log());
}
See also:
The trait cannot be made into an object
Unable to create a polymorphic type because the trait cannot be made into an object
How to implement `serde::Serialize` for a boxed trait object?
How can deserialization of polymorphic trait objects be added in Rust if at all?
Using trait inheritance works, but using the right Serde trait, not the compiler-suggested one:
trait Log: serde::Serialize {
fn to_log(&self) -> String {
serde_json::to_string(&self).unwrap()
}
}