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

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.

Related

Avoid duplicating use statements at beginning of doctests

When writing doctests for methods on a struct or trait or functions in a file, etc. I often find myself putting the same use my_crate::{MyStruct, MyTrait, Etc} at the beginning of each doctest. Is there any way to avoid this duplication, like some way I could define those use statement just once for the whole module?
If you keep finding the same group of items gets imported over and over again, you may want to consider creating a prelude module. The core idea is that you simply re-export those key items in that module so anyone using your crate can do use your_crate::prelude::*; to import all of them as a group.
One common use cases for this is if you have a lot of traits that are frequently used. When you provide the most common traits as a group, your users don't need to spend figuring out which traits provide which methods. You can also choose to add structs/enums/unions, but I wouldn't recommend it. Unlike traits, types are almost always referred to explicitly and are much easier to find.
For example, here is what rayon's prelude module looks like.
//! The rayon prelude imports the various `ParallelIterator` traits.
//! The intention is that one can include `use rayon::prelude::*` and
//! have easy access to the various traits and methods you will need.
pub use crate::iter::FromParallelIterator;
pub use crate::iter::IndexedParallelIterator;
pub use crate::iter::IntoParallelIterator;
pub use crate::iter::IntoParallelRefIterator;
pub use crate::iter::IntoParallelRefMutIterator;
pub use crate::iter::ParallelBridge;
pub use crate::iter::ParallelDrainFull;
pub use crate::iter::ParallelDrainRange;
pub use crate::iter::ParallelExtend;
pub use crate::iter::ParallelIterator;
pub use crate::slice::ParallelSlice;
pub use crate::slice::ParallelSliceMut;
pub use crate::str::ParallelString;

Is there a way to automatically register trait implementors?

I'm trying to load JSON files that refer to structs implementing a trait. When the JSON files are loaded, the struct is grabbed from a hashmap. The problem is, I'll probably have to have a lot of structs put into that hashmap all over my code. I would like to have that done automatically. To me this seems to be doable with procedural macros, something like:
#[my_proc_macro(type=ImplementedType)]
struct MyStruct {}
impl ImplementedType for MyStruct {}
fn load_implementors() {
let implementors = HashMap::new();
load_implementors!(implementors, ImplementedType);
}
Is there a way to do this?
No
There is a core issue that makes it difficult to skip manually inserting into a structure. Consider this simplified example, where we simply want to print values that are provided separately in the code-base:
my_register!(alice);
my_register!(bob);
fn main() {
my_print(); // prints "alice" and "bob"
}
In typical Rust, there is no mechanism to link the my_print() call to the multiple invocations of my_register. There is no support for declaration merging, run-time/compile-time reflection, or run-before-main execution that you might find in other languages that might make this possible (unless of course there's something I'm missing).
But Also Yes
There are third party crates built around link-time or run-time tricks that can make this possible:
ctor allows you to define functions that are executed before main(). With it, you can have my_register!() create invididual functions for alice and bob that when executed will add themselves to some global structure which can then be accessed by my_print().
linkme allows you to define a slice that is made from elements defined separately, which are combined at compile time. The my_register!() simply needs to use this crate's attributes to add an element to the slice, which my_print() can easily access.
I understand skepticism of these methods since the declarative approach is often clearer to me, but sometimes they are necessary or the ergonomic benefits outweigh the "magic".

How to hide trait implementation in 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.

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.

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