How to hide trait implementation in Rust - rust

I have a project with its own error type, which is exposed outside of crate. Lets call it MyErrorType. The project itself internally depends on another crate with its own error type. Lets call it ForeignErrorType.
In order to simplify code and make it more readable I've created following implementation of From trait:
impl From<ForeignErrorType> for MyErrorType {
...
}
This allows to use question mark operator, ?, when dealing with foreign error types without a necessity to convert them in place.
The issue is that the mentioned trait implementation is exposed outside of my crate. I don't want the users of my crate to accidentally rely on the conversion possibility from ForeignErrorType to MyErrorType.
What I've tried so far: Have put the mentioned trait implementation into module with pub(crate) visibility. This surprisingly hides all structs defined in such module, but leaves trait implementation exposed.
Is there way to keep my From implementation private and not to expose it outside of crate?
Probably I'm trying to achieve error handling benefits in a non‑idiomatic way. If this is true, what's the proper way to be able to use ? operator on foreign error types without exposing them?

Is there way to keep my From implementation private and not to expose it outside of crate?
No. Trait implementations have no scope.
What you can do instead in this case is write a function with the same contents as your From implementation would have had, and apply it with Result::map_err before ?.
pub(crate) fn foreign_err(e: ForeignErrorType) -> MyErrorType {
todo!()
}
...
let foo = their_function().map_err(foreign_err)?;
This has to be done at every ? usage, but there is no way to have an implicit conversion that is scoped to a crate or module, so that's the best you can have.

Related

What is the scope of a trait implementation on a type from the standard library in Rust?

The rust book says:
We can also implement Summary on Vec<T> in our aggregator crate,
because the trait Summary is local to our aggregator crate.
https://doc.rust-lang.org/book/ch10-02-traits.html#implementing-a-trait-on-a-type
If my package uses another crate from crates.io, like rand, and rand implements a trait on a type in the standard library, like Vec<T>, will my code be able to see those methods?
I know there's a restriction where a trait has to be in scope for you to use its methods. If rand implemented a custom trait on Vec<T>, and I tried to use one of the methods in that trait within my crate, would the compiler tell me that I need to import that trait from rand before using those methods, or would it tell me that the methods don't exist? If it's the former, if I import the trait from rand, can I then use those methods on Vec<T>?
From my experimentation, if a crate implements a trait on a foreign type, that trait is accessible using the normal rules (that is, in order to call methods of that trait, you must bring it in into scope, but otherwise, nothing special is required). You don't need to do anything else.
For example, consider the crate serde, which provides facilities to serialize and deserialize data. It provides the traits Serialize and Deserialize, which allow data structures to define how they are serialized and deserialized into various formats. Additionally, it provides implementations of those traits for many built-in and std types (see here and here). I made a quick experiment here, and my code can use those traits without me having to do anything extra (in fact, as you'll see, since I never directly use those traits, I don't even have to bring them into scope).
So, to the best of my knowledge, the answer to your question is yes, your code can use a trait implemented by rand for Vec<T>. All you need to do is import that trait from rand.

How to avoid code duplication using traits in Rust? [duplicate]

Traits are used to group some functions to be implemented from a struct, but is it possible to access struct fields from within the trait?
I could imagine declaring fields inside the trait so that the fields are abstracted as well. I haven't found such a syntax; is there any other solution? Otherwise, it wouldn't be possible to have non-static methods using a trait, would it?
I know object oriented programming from C# and I'm playing around with Rust, trying to adapt the OOP functionality I already know from C#.
This sounds like you're misunderstanding how traits work. Traits can't have fields. If you want to provide access to a field from a trait, you need to define a method in that trait (like, say, get_blah).
If you're asking whether you can access fields of a struct from within that struct's implementation of a trait, then yes. The struct knows it's own type, so there's no problem.
trait Pet {
fn is_smelly(&self) -> bool;
}
struct Dog {
washed_recently: bool,
}
impl Pet for Dog {
fn is_smelly(&self) -> bool {
!self.washed_recently
}
}
If you're writing a default implementation of a trait (i.e. defining a method body within the trait), then no, you can't access fields. A default implementation can only use methods that are defined on the trait or in a super trait.
It would be useful to define fields in a trait's default implementation, so a struct implementing the trait would always have the same fields.
Apparently, the Rust team thinks the same but it is still a work in progress according to this RFC. It's a big change and it has been postponed, so I think the short answer is: you can't do it yet, but you might be able to do it in the future.
For now, you'll have to make do with less powerful traits.
You can make accessor function in default trait implementation, that must return field value/ref in child implementations, returning default value. Use it in other fn's in default implementation, and redefine accessor's in child implementation. Default implementation fn's will use redefined accessors as it's virtual fn's.

Implement foreign trait for foreign type [duplicate]

This question already has answers here:
How do I implement a trait I don't own for a type I don't own?
(3 answers)
Closed 7 years ago.
So I see there are reasons to forbid orphan trait implementations because of forward compatibility considerations (to prevent the addition of a further trait implementation in a library from breaking somewhere where the type's trait is used) and it possibly making compilation a lot more difficult. But I wonder which workaround would be considered by the Rust community to be the most ideal one:
(Just in case this wasn't enough background: I am trying to use rusqlite with chrono's DateTime. So I want to implement rusqlite's FromSql and ToSql traits for DateTime<UTC> but that's apparently not as easy as I thought it'd be - I'm just starting out with Rust currently.)
Fork rusqlite and implement the trait. (I kind of feel like this is not the nicest way to do it because maybe it's just me who needs this trait implementation there and so I might end up having to keep my own fork up to date. Also I wasn't able to implement the traits because there are some complicated generics things that I don't understand perfectly yet.)
Implement my own DateTime struct (probably the best workaround but I feel like this is just some replication of work that shouldn't be nescessary).
Somehow "copy" the DateTime<UTC> trait and give it an alias and implement the FromSql and ToSql traits for my aliased type (however I think this is not trivial either and when I tried it I wasn't able to get it to work since it was still treated as an external type).
I hope somebody can explain to me how to best solve this issue, from my pure OOP experience I would just like to be able to inherit DateTime and implement the interface but (for valid reasons) this is not how it gets done in Rust...
The simplest way would be to use newtype pattern:
extern crate a;
extern crate b;
use a::SomeTrait;
use b::SomeStruct;
pub struct MySomeStruct(SomeStruct);
impl SomeTrait for MySomeStruct {
...
}
Here you create a wrapper around the foreign struct, and since this wrapper is a completely different type which belongs to your crate, you are free to implement a::SomeTrait for it. This is similar to your 2nd point, except that you absolutely don't need to reimplement the type from scratch.
Naturally, you won't be able to call all methods of SomeStruct on MySomeStruct. You must either forward all methods you want, or unwrap the inner value when you don't need its trait implementation anymore, or you can impl Deref for MySomeStruct { type Target = SomeStruct; ... }, but the latter is considered an antipattern.
I'm not sure what would be the most idiomatic, but it looks like the best approach would be to use the newtype pattern, which is a tuple-struct with one field. This creates a new type distinct from the old one, and you can implement the traits for that new type. To use the trait methods, you will need to wrap it in the newtype, but to use the normal methods, you'll use the normal struct without your newtype wrapper.
struct MyType(TheirType);
impl TheTrait for MyType {
....
}
fn main() {
let a = TheirType::new(....);
a.method_on_their_type();
let b = MyType(TheirType::new(....));
b.method_on_the_trait();
}

Is it required to import a trait whenever it is used?

I want to be able to have a trait which I can implement multiple times but not need to explicitly import it each time when using the library.
I have a lib.rs file which looks like this:
pub mod learning {
pub mod lin_reg;
pub trait Model<T,U> {
fn predict(&self, data:T) -> U;
fn train(&mut self, data:T, value:U);
}
}
And then I implement this Model trait in each of my models (like the Linear Regression model).
Now if the user wants to use the linear regression model they will need to do the following:
use ...::learning::lin_reg::LinRegressor;
use ...::learning::Model;
let model = LinRegressor:new();
model.train(...)
I want to be able to remove the need to import the Model trait each time. Is it possible to do this? Will I need to restructure my library?
Yes, it is always required that the end-user has explicitly declared that a trait is to be used. You can see examples of this in the standard library, as the std::io::Read trait is not available by default and must be used.
You can also follow the IO modules lead here, as it defines a prelude. This is a module called prelude which re-exports common traits and structs.
There's even a prelude for the entire standard library. The only difference is that the compiler automatically adds this for you, unless you use the no_std feature.
You can do the same for your crate, if you expect that everyone will want to use certain traits if they use it at all.
A bit of editorial, but I really prefer this style. As an end-user, I can have a very good sense of what it is that I am importing and relying on. This helps me, the programmer, keep track of my dependencies and make sure that my code isn't gathering "secret" dependencies that ultimately decrease the organization of my code.

Do I have to 'use' a trait in order to call methods defined in that trait?

I'm toying with the cgmath library. I have the following main.rs file:
extern crate cgmath;
use cgmath::vector::{Vector3, EuclideanVector};
fn main() {
let mypoint = Vector3 { x: 1f64, y: 1f64, z: 3f64 };
println!("The length of the vector is {}, and the length squared is {}", mypoint.length(), mypoint.length2());
}
In my use line, when I omit EuclideanVector, I am given the following compilation error:
type 'cgmath::vector::Vector3<f64>' does not implement any method in scope named 'length'
It appears that the Rust compiler cannot find the length() method unless I import one of the traits that Vector3 uses. Delving into the source code, it looks like the length method is defined within the EuclideanVector trait.
Intuitively, I should not need to import an trait to use a type that inherits said trait. Is there a technique to do so that I am missing? Is this a nuance specific to the cgmath library? Is this an idiomatic part of Rust that I should become accustomed to?
You're thinking of traits in terms of inheritance. It might make more sense if you think of a trait as a module that's overloadable with respect to the Self type. From this perspective, it makes sense that the trait has to be in scope in order for the compiler to know about its methods, just as a module has to be in scope in order to use it. A particular implication of this is that implementations can be declared alongside the trait they implement rather than the type they implement it for. In this situation, clearly if your code doesn't know about the trait, it can't use its methods.
Another motivation for the current behaviour is that multiple traits can define methods with the same name, and when you have such a conflict for traits implemented for the same type, you can no longer use method call syntax to access their methods. Instead, you have to use function call syntax in order to specify which trait the method is a member of (the trait acting as the module the method is in). If method call syntax used all traits in your program rather than just the ones in scope for method resolution, you'd end up with these conflicts much more often, since you'd have name conflicts with methods in traits that your code isn't actually using directly.
Strictly speaking, you don't have to use use. Alternatively:
(&mypoint as &cgmath::vector::EuclideanVector).length2()
Yes, this is how Rust works. You must always import a trait before you can use its methods. This is by design.
If you really don't want to import, you can call cgmath::vector::EuclideanVector::length(&mypoint).
(I don't know if this was possible when the question was asked.)

Resources