Is it possible to define a trait like the following one:
pub trait DataValidator {
fn validate_data(&self, data: TBD) -> bool;
}
where the TBD type can be different data types like u32, Vec<u8>, Vec<Vec<u8>>, ... and have a different implementation for different enums that implement that trait?
The idea is to create a validator for different data types.
I have the following enum:
pub enum DataType {
Text,
Number,
Array,
}
and I have an struct that uses that enum
pub struct Validator {
pub data_type: DataType
}
I want to implement the trait for Validator, but the input that the validate_data function is not the same all the time, or at least that's what I want to cover.
So for example, let's say I have a Validator name validator with data_type set as Text. I want to then call the validate_data function like validator.validate_data(data) where data is a Vec<u8>. It only has to validate that the length of the Vec is within a certain limits.
But let's say I have another Validator named validator2 with data_type set as Array. I want to pass to the validate_data function an array of different DataTypes and validate each one of them using the same logic, but for that I need the trait to be flexible with the input type. Is it possible?
You can implement dummy trait SupportedDataType just to indicate types that you support and connect each DataValidator trait with some type (ValidatedDataType) which will be trait SupportedDataType then you can change type for every implementation (which as I understood is required feature).
trait SupportedDataType {}
impl SupportedDataType for String {}
impl SupportedDataType for i32 {}
pub trait DataValidator {
type ValidatedDataType: SupportedDataType;
fn validate_data(&self, data: Self::ValidatedDataType) -> bool;
}
struct Validator {}
impl DataValidator for Validator {
type ValidatedDataType = u64;
fn validate_data(&self, data: Self::ValidatedDataType) -> bool {
return true;
}
}
In specific implementation you need to provide specific type.
Related
trait MyTrait {
fn my_f(&self) ;
}
struct A {}
struct B {}
impl MyTrait for A {
fn my_f(&self) {
println!("A.my_f()");
}
}
impl MyTrait for B {
fn my_f(&self) {
println!("B.my_f()");
}
}
struct MyList<T: MyTrait> {
list: Vec<T>
}
fn main() {
let list = MyList {
list: vec![
Box::new(A{}),
Box::new(B{})
]
};
}
I followed this article, Using Trait Objects That Allow for Values of Different Types
According to this article:
This restricts us to a Screen instance that has a list of components all of type Button or all of type TextField. If you’ll only ever have homogeneous collections, using generics and trait bounds is preferable because the definitions will be monomorphized at compile time to use the concrete types.
On the other hand, with the method using trait objects, one Screen instance can hold a Vec that contains a Box as well as a Box. Let’s look at how this works, and then we’ll talk about the runtime performance implications.
But this code can't be compiled.
Read more closely the section on Defining a Trait for Common Behavior. It defines Screen in listing 17-4 with a Vec<Box<dyn Draw>>:
pub struct Screen {
pub components: Vec<Box<dyn Draw>>,
}
Then it says:
we could have defined the Screen struct using a generic type and a trait bound as in Listing 17-6
Emphasis on the conditional "could". It then goes on to explain why using a generic type and trait bound doesn't work:
This (1) restricts us to a Screen instance that has a list of components all of type Button or all of type TextField.
[…]
On the other hand, with the method using trait objects (2), one Screen instance can hold a Vec<T> that contains a Box<Button> as well as a Box<TextField>.
(1) using a generic type and trait bound
(2) i.e. with Box<dyn<Draw>>
I'm struggling to wrap my head around usage of traits when these have associated types.
Here's a very simplified example:
pub trait Message {}
pub trait SendsMessages {
type Message: Message;
fn send(msg: Self::Message);
}
pub struct ImplementsSendsMessages {
}
impl<T> SendsMessages for ImplementsSendsMessages
where
T: Message,
{
type Message = T;
fn send(id: T) {
todo!()
}
}
Can anyone point me to what I'm doing wrong?
Whenever you write impl<T>, you're defining a generic implementation — a family of possible implementations. For every type you can fill in for T, that's a different implementation.
But every trait implementation must be uniquely identifiable from the trait and the implementing type: you can't have more than one SendsMessages for ImplementsSendsMessages, and that's what your code is trying to create.
You can define the trait with a type parameter instead of an associated type:
pub trait Message {}
pub trait SendsMessages<M: Message> {
fn send(msg: M);
}
pub struct ImplementsSendsMessages {}
impl<T> SendsMessages<T> for ImplementsSendsMessages
where
T: Message,
{
fn send(id: T) {
todo!()
}
}
This is accepted because there's a different trait SendsMessages<T> for each T that exists, so it's unambiguous.
(It would also work if you had struct ImplementsSendsMessage<T>, since that's also unambiguous.)
Another way to look at this is that the purpose of associated types as opposed to type parameters is to be “outputs”, rather than “inputs”: once you pick a trait, an implementing type, and fill in all the parameters, the associated type is always known and unique. Your code fails because it's trying to fill in the associated type with “anything”, when it has to be a single concrete type.
The Rust compiler doesn't allow specifying new type parameters on Self in the return types of trait methods. That is, a trait method can return Self, but not Self<T>. I'm interested in what the proper way is to accomplish the following; or if it's fundamentally impossible, then why that's the case.
Suppose we have two structs as follows:
pub struct SomeStruct<T> {
id: usize,
description: String,
extra_data: T,
}
pub struct OtherStruct<T> {
permit_needed: bool,
registration_number: usize,
extra_data: T,
}
Both of these structs have an extra_data field of type T, where T is a generic type parameter. We can implement methods such as get_extra_data(), and more interestingly, transform(), on both structs:
impl<T> SomeStruct<T> {
pub fn get_extra_data(&self) -> &T {
&self.extra_data
}
pub fn transform<S>(&self, data: S) -> SomeStruct<S> {
SomeStruct {
id: self.id,
description: self.description.clone(),
extra_data: data,
}
}
}
impl<T> OtherStruct<T> {
pub fn get_extra_data(&self) -> &T {
&self.extra_data
}
pub fn transform<S>(&self, data: S) -> OtherStruct<S> {
OtherStruct {
permit_needed: self.permit_needed,
registration_number: self.registration_number,
extra_data: data,
}
}
}
But I'm having trouble creating a trait Transformable that unifies these two types with respect to these operations:
trait Transformable<T> {
fn get_extra_data(&self) -> &T;
fn transform<S>(&self, data: S) -> Self<S>;
}
The transform() function needs to return something of the same type as Self, just with a different type parameter. Unfortunately, the rust compiler rejects this with the error message E0109: type arguments are not allowed for this type.
Even the weaker signature fn transform<S>(&self, data: S) -> impl Transformable<S> is rejected, with error E0562: impl Trait not allowed outside of function and inherent method return types. The traditional solution here, to my knowledge, would be to return an associated type instead of an impl, but that would require generic associated types in this case. And at any rate that would seem much more roundabout than the stronger Self<S> signature which is what we really want.
Playground link with the above code
An analogous problem happens exactly the same way with lifetime parameters. I'm actually more interested in the lifetime parameters case, but I thought the type parameters case might be easier to understand and talk about, and also make it clear that the issue isn't lifetime-specific.
Consider the following Rust code
trait Trait {
fn show_name(&self) {}
}
struct Foo {
name: String,
things_and_stuff: usize,
}
impl Trait for Foo {
fn show_name(&self) {
println!("{}", self.name);
}
}
struct Bar {
name: String,
other_field: i32,
}
impl Trait for Bar {
fn show_name(&self) {
println!("{}", self.name);
}
}
The two show_name functions have exactly the same code. It would be convenient if I could put that method as a default method on Trait, but that's not possible because traits cannot access struct fields.
We could declare a get_name(&self) -> &str method on Trait and implement it on Foo and Bar, but that doesn't fix the problem of having duplicated code because both implementations of get_name will be the same.
It would be nice to avoid the code duplication. Another question has already asked if field access is possible in traits, and the answer was basically "no".
However, I found a comment in the rust-internals forum suggesting that it's already possible. Here's the code:
struct Fields {
name: String,
}
trait Trait: BorrowMut<Fields> {
// methods go here
}
impl<T> Trait for T where T: BorrowMut<Fields> {}
Presumably there's a way to make a type T be BorrowMut<Fields> and use that to allow Trait to access Fields's fields, but so far I'm not sure how that would work.
How is the code snippet shown above supposed to solve the problem of getting field access in traits?
I know there are discussions of adding field access in traits to the language (rust-internals, RFC, another RFC), but I'd like to know what's possible now.
the answer was basically "no"
The answer actually says (emphasis mine):
A default implementation can only use methods that are defined on the trait or in a super trait.
That's what your snippet does:
trait Trait: BorrowMut<Fields>
To make it work, follow the advice from the post you are referencing:
all types which choose to implement BorrowMut<Foo>
Thus, you need to implement the trait for each of your types. I switched to Borrow because you don't need mutability here:
use std::borrow::Borrow;
struct Fields {
name: String,
}
trait Trait: Borrow<Fields> {
fn show_name(&self) {
let fields: &Fields = self.borrow();
println!("{}", fields.name);
}
}
struct Foo {
fields: Fields,
things_and_stuff: usize,
}
impl Borrow<Fields> for Foo {
fn borrow(&self) -> &Fields {
&self.fields
}
}
struct Bar {
fields: Fields,
other_field: i32,
}
impl Borrow<Fields> for Bar {
fn borrow(&self) -> &Fields {
&self.fields
}
}
impl<T> Trait for T where T: Borrow<Fields> {}
I'm almost certain you won't like this solution because it
doesn't fix the problem of having duplicated code because both implementations [...] will be the same
You may prefer to write a macro if your goal is to reduce the number of duplicate characters in your code.
See also:
I implemented a trait for another trait but cannot call methods from both traits
Is it possible to access struct fields from within a trait?
Defining the methods of generic type requires adding generic types after impl:
struct GenericVal<T>(T,);
impl <T> GenericVal<T> {}
I feel that removing <T> seems OK:
struct GenericVal<T>(T,);
impl GenericVal<T> {}
Is it any special consideration?
Rust allows you to write impl blocks that apply only to some specific combination of type parameters. For example:
struct GenericVal<T>(T);
impl GenericVal<u32> {
fn foo(&self) {
// method foo() is only defined when T = u32
}
}
Here, the type GenericVal is generic, but the impl itself is not.
Thus, if you want to write an impl block that applies for all GenericVal<T> types, you must first declare a type parameter on the impl itself (otherwise, T would try to look up a type named T).
struct GenericVal<T>(T);
impl<T> GenericVal<T> {
fn foo(&self) {
// method foo() is always present
}
}
This declaration also lets you have a single type parameter that can be used multiple times, forcing the types to be the same.
struct GenericVal<T, U>(T, U);
impl<V> GenericVal<V, V> {
fn foo(&self) {
// method foo() is only defined when T = U
}
}