Call async code from sync impl of trait in Rust - rust

I am developing my first toy project that consists in a simple API with the Rocket framework. But I am stuck with a very basic need and I would love to have you insights on how to solve it.
For some endpoint, I need to call an external REST API, so I am using the reqwest crate to do so. I prefer doing it async because I expect my API to have a lot of concurrent requests on that endpoint.
On the other side, I implement the whole think using clean/hexagonal architecture guidelines and then I use traits as a kind of interface. This helps for testing and mocking amongst other things.
How does it looks like:
pub trait DataFetcher {
fn fetch_data(path: String) -> Result<String, Box<dyn sts::error::Error>>,
}
pub struct DataProvider {
}
impl DataProvider {
pub fn new() -> Self {
// constructor here.
}
}
impl DataFetcher for DataProvider {
pub fetch_data(path: String) -> Result<String, Box<dyn std::error::Error>> {
// call to external REST API should be here.
}
}
The problem comes here: async trait methods are not supported in Rust 2018. Here are my guesses:
Stop using traits
Use Async_traits crate (experimental)
Other ?
What is you advice? Do you have some example projects that I can be inspired from?

The async-trait crate is definitely not experimental. It is one of the most widely used crates, and is the standard solution for async fns in traits.
However, I would not use traits if you don't need the abstraction. This just complicates the code.

Related

Re-implement Debug trait for a third-party struct in Rust

I have an autogenerated struct named Address by protobuf in Rust. It has a predefined Debug trait like this:
impl ::std::fmt::Debug for Address {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
::protobuf::text_format::fmt(self, f)
}
}
But I want to customize how it's printed when {:#?} is used. So I decided to implement Debug trait for it like this in my project:
impl fmt::Debug for EvmProto::Address {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
....
}
}
But it complains that conflicting implementations of trait `std::fmt::Debug` for type `protos::Evm::Address
UPDATE: while the answer can still be useful in some similar cases, in this case it seems incorrect. See the comments for details.
You can't implement an external trait on an external type.
I'm citing The Rust Book
we can’t implement external traits on external types. For example, we can’t implement the Display trait on Vec<T> within our aggregator crate, because Display and Vec<T> are both defined in the standard library and aren’t local to our aggregator crate. This restriction is part of a property called coherence, and more specifically the orphan rule, so named because the parent type is not present. This rule ensures that other people’s code can’t break your code and vice versa. Without the rule, two crates could implement the same trait for the same type, and Rust wouldn’t know which implementation to use.

How can I use a trait implementation from another crate in the original crate?

I have a Rust project which divided among several crates in a workspace. One of these crates is a test crate which holds utilities for use in unit and integration tests within the other crates.
In one crate I define a trait that is implemented by a struct in the test crate. However, when I try to use the struct from the test crate in the original crate which defined the trait, I encounter errors when trying to use the trait's member functions.
Here is a short example:
In the project-trait crate:
trait Trait {
fn do_something(&self)
}
In the project-test crate:
use project_trait::Trait;
pub struct TestObject;
impl Trait for TestObject {
fn do_something(&self) {
// ...
}
}
Finally, back in the project-trait crate:
#[cfg(test)]
mod test {
use crate::Trait;
use project_test::TestObject;
#[test]
fn use_test_object() {
let object = TestObject;
object.do_something();
}
}
When running cargo test in the project-trait crate, I get error E0599 which says I should import project_trait::Trait to use the do_something method. It seems Rust doesn't see that crate::Trait and project_trait::Trait are the same trait.
Is there any workaround for this?
There can't be cycles in the crate dependency graph. Therefore, whatever you've actually done in your project configuration, it can't be that the #[cfg(test)] code that is depending on project-test is using the same crate::Trait as what project-test sees as project_trait::Trait.
You have probably done something that causes some of the code to be compiled as part of a different crate (such as misuse of mod so as to compile the same source file in two crates). The result of this is that you have two different traits that happen to both be named Trait. Thus, you get an error because the one that TestObject implements is not the same as the one you have imported.
(In order to get a more precise explanation of what's going wrong, you'll need to provide a more complete example — preferably reproducible, i.e. "if I copy these files into a local folder they will be sufficient to demonstrate the problem".)
In general, whatever code is needed to run project-trait's tests must be part of the project-trait crate or its dependencies — you can't split out test helpers into an independent library crate.

How can I create a trait for the inherent methods of a type I don't own?

I'd like to implement my own version of a Rust standard library feature and use my version and the real standard library version interchangeably.
If the standard library feature were a trait, this would be easy. However, the standard library feature (in this case, std::sync::Condvar) is implemented as
pub struct Condvar {...}
impl Condvar {...}
I tried doing
impl Condvar for MyCondvar {...}
but got an error ("error[E0404]: expected trait, found struct Condvar")
How should I do this? I've also tried
pub trait CondvarTrait { // copy entire interface of Condvar }
impl CondvarTrait for Condvar {
// copy entire interface of Condvar again,
// implementing foo(&self, ...) by calling self.foo(...)
}
impl CondvarTrait for MyCondvar { // my own implementation }
which compiles, but is super verbose. Is there a better way?
You need to copy the entire interface of the type and reimplement it by calling the inherent methods.
See also:
How to call a method when a trait and struct use the same method name?
Is there a better way?
Not with how you've described the requirements.
That being said, you aren't introducing any abstraction suited to your domain. In many cases, you don't want the full power of the underlying type(s), but want to do some higher-level abstraction. If you create that abstraction as a trait, then you can implement the new trait for any useful types.

Should library expose c_int to consumers or force i32 compatibility

I'm writing a simple Rust wrapper over C++ library. And this library is written using native int type. And I don't know what's the best approach to expose these API via rust. Here we have two considerations:
Library is just a wrapper over C++ code and it's not our responsability to check int size. Thus, just expose c_int in our high level interfaces.
High level interfaces must use native Rust types such as i32 so we use it everywhere while statically checking that int is i32 via some healthcheck like
#[allow(unused)]
fn assert_32_bit() {
let _: c_int = 0_i32;
}
The latter just forbid user to use this library anywhere int is not i32.
I have read nomicon opinion about this problem (link), and its opinion is:
The raw C API needs to be wrapped to provide memory safety and make
use of higher-level concepts like vectors.
But all examples with safe wrappers are using int32_t and similar types, that are easy to map on Rust type system.
Which approach should I take, and why? What's the official community position about this question?
For example, here is sample C++ function:
int sum(int a, int b);
Should I write
fn high_level_api_sum(a: c_int, b: c_int) { unsafe {sum(a, b)} }
or
fn high_level_api_sum(a: i32, b: i32) { unsafe {sum(a, b)} }
#[allow(unused)]
fn assert_32_bit() {
let _: c_int = 0_i32;
}
I don't think there is anything remotely like an "official" position on this. What follows is opinion... perhaps suggesting this question isn't suitable for SO in the first place.
If the correct type is c_int, use c_int. Rust programmers shouldn't be so very fragile that they curl into the fetal position just because you use a C type in an interface to a C/C++ library.
Ask yourself this: do your users benefit from you locking out any and all potential platforms where c_int is not i32 (e.g. embedded platforms)?
If the answer is "they don't", then don't do it. If they do benefit somehow, well, you'll have to weigh that against locking out platforms yourself. Maybe the use of c_int imposes some kind of wider interface issue.

Is there a way other than traits to add methods to a type I don't own?

I'm trying to extend the Grid struct from the piston-2dgraphics library. There's no method for getting the location on the window of a particular cell, so I implemented a trait to calculate that for me. Then, I wanted a method to calculate the neighbours of a particular cell on the grid, so I implemented another trait.
Something about this is ugly and feels unnecessary seeing as how I'll likely never use these traits for anything other than this specific grid structure. Is there another way in Rust to extend a type without having to implement traits each time?
As of Rust 1.67, no, there is no other way. It's not possible to define inherent methods on a type defined in another crate.
You can define your own trait with the methods you need, then implement that trait for an external type. This pattern is known as extension traits. The name of extension traits, by convention, ends with Ext, to indicate that this trait is not meant to be used as a generic bound or as a trait object. There are a few examples in the standard library.
trait DoubleExt {
fn double(&self) -> Self;
}
impl DoubleExt for i32 {
fn double(&self) -> Self {
*self * 2
}
}
fn main() {
let a = 42;
println!("{}", 42.double());
}
Other libraries can also export extension traits (example: byteorder). However, as for any other trait, you need to bring the trait's methods in scope with use SomethingExt;.
No. Currently, the only way to write new methods for a type that has been defined in another crate is through traits. However, this seems too cumbersome as you have to write both the trait definition and the implementation.
In my opinion, the way to go is to use free functions instead of methods. This would at least avoid the duplication caused by traits.

Resources