Generic APIs with traits - rust

I'm attempting to generalize/decouple an API, so that alternative structs that satisfy a Trait can be used. Struggling on Syntax regarding nested traits. This is a stripped down example of what I'm attempting to do. Note that in the real one, there are multiple sub-traits, which potentially makes a follow-on question of "how to reduce verbosity, if I'm on the right track".
pub mod general {
/// A trait to make the API generic
pub trait MyTrait<A: PartialEq> {
// type A: Partial
fn val(self) -> A;
}
/// Part of the generic API
pub struct Data<A: PartialEq, T: MyTrait<A>> {
mys: T
}
/// Another part of the generic API
fn doit<A: PartialEq>(s: impl MyTrait<A>) -> impl MyTrait {
s
}
}
pub mod specific {
/// Api-specific substruct
#[derive(PartialEq)]
pub struct Aval {
val: u32
}
/// My top-level custom struct
pub struct MyS {
val: Aval
}
}
impl<A: PartialEq> crate::general::MyTrait<A> for crate::specific::MyS {
fn val(self) -> crate::specific::Aval {
self.val
}
}
/// Example of how we'd use this
fn main() {
let mys = crate::specific::MyS{ val: crate::specific::Aval{ val: 0 } };
let S = crate::general::Data{mys};
crate::general::doit(mys); // Eg pretend we cloned or didn't create S.
}
Playground
In this specific example, we have a chicken+egg: Error on the Data struct of error[E0392]: parameter `A` is never used. Where if we remove A: error[E0412]: cannot find type `A` in this scope
I suspect this is a syntax problem related to associated types.

Just remove the trait bound from your struct.
struct Data<T> {
mys: T
}
You can still add methods and trait implementations for Data<T> requiring more specific bounds on T, but you don’t need them to define the layout of Data<T>, so you shouldn’t specify them there (and in this case, you can’t, unless you add a PhantomData member referring to A).
Your code has a number of other errors, but I trust you’ll figure them out. Feel free to comment if you get stuck again.

Related

Pattern matching for get the desired type on a impl statement

I have these two types:
pub struct A;
pub struct B;
The following trait:
pub trait MyTrait {}
and a free function:
fn do_something(mut element: impl MyTrait) { ... }
I want to, based on the real type behind the impl, do something different depending the type of the element that is coming in the current function call.
// pseudo-snippet
match element {
A => do something with A,
B => do something with B
}
Is possible in Rust, determine the type behind an impl statement? Or, at least, make some decision making workflow based (if-else branches, patter matching...) on that impl?
The way to do this is to add a method to MyTrait, which does the two different things you want, or which returns an enum that you can match.
pub trait MyTrait {
fn do_something(self);
}
There is no mechanism to directly branch on what a concrete type is in the way you mean. Technically you could achieve it with std::any::TypeId, but you shouldn't — because you're defeating static checking, making it possible for an error in your program (not handling the right set of types) to be delayed until run time. Instead, by putting the functionality into MyTrait, your program has the property that if some type implements MyTrait, there is necessarily a definition of what do_something should do with that trait.
There is no syntax for match on types. The only way to do any kind of "downcasting" is via the Any trait. You can use it with an impl MyTrait if it also constrained to be 'static:
use std::any::Any;
pub struct A;
pub struct B;
pub struct C;
pub trait MyTrait {}
impl MyTrait for A {}
impl MyTrait for B {}
impl MyTrait for C {}
fn do_something(mut element: impl MyTrait + 'static) {
let element = &mut element as &mut dyn Any;
if let Some(a) = element.downcast_mut::<A>() {
// do something with `a`
println!("is A");
} else if let Some(b) = element.downcast_mut::<B>() {
// do something with `b`
println!("is B");
} else {
// do something with `element`
println!("is unknown");
}
}
fn main() {
do_something(A);
do_something(B);
do_something(C);
}
is A
is B
is unknown
The two caveats with this code are 1) the 'static constraint, meaning you can't use this with traits or implementations with non-static generic lifetimes; and 2) you can't get an owned version of A or B (the type of a is &mut A in the above code).
This type of code pattern is very un-idiomatic. If you want to have trait instances exhibit different behavior based on their implementations, you should have an associated method for it:
pub trait MyTrait {
fn do_something(self)
}
Or if you want to implement behavior on a closed-set of types with shared behavior, you should instead use an enum:
enum MyEnum {
A(A),
B(B),
}
fn do_something(element: MyEnum) {
match element {
MyEnum::A(a) => { println!("is A"); },
MyEnum::B(b) => { println!("is B"); },
}
}
With either of these methods, you don't have the same caveats of trying to circumvent the polymorphic trait system.
See also (none address the impl Trait case, but they pretty much all say the same thing):
How to match trait implementors
How do you match a trait in rust?
How to match on data type in Rust?

the type parameter `T` is not constrained by the impl trait, self type, or predicates

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.

How to access fields of dyn structs?

Recently I've been working on learning advanced Rust. As part of that, I'm learning to use dynamic dispatch.
In my tinkering I've run across a bit of an issue. For some reason, I can't seem to access fields of structs which have been assigned to variables using Boxes and dynamic dispatch. For example,
fn main() {
let z: Box<dyn S>;
z = Box::new(A::new());
println!("{}", z.val);
}
trait S {
fn new () -> Self where Self: Sized;
}
struct A {
val: i32,
}
impl S for A {
fn new () -> A {
A {val: 1}
}
}
struct B {
val: i32
}
impl S for B {
fn new() -> B {
B {val:2}
}
}
yields the error message "error[E0609]: no field val on type Box<dyn S>"
Is there any way to access such fields, or do I need to kluge together a workaround?
It is easy to understand why this does not work if you understand what a trait object is. When a method returns a dyn Trait it does not return an instance of any struct. Instead it returns a lookup table, which tells the caller where to find its methods. So the caller can access methods without knowing anything about underlying struct itself.
So if the caller does not have access to the struct itself it's clear it cannot access its fields.
There are two ways to implement what you are trying to do:
Use methods of the trait to access required fields. This is a good option if the list of possible implementors of the trait is not predetermined, and if the trait itself is not too complex (as traits that can be represented as trait objects have some limitations). Not though that trait objects come with some runtime overhead (using lookup tables is slower then direct method access).
Use enum. If you know complete list of options that the method can return, it's the best and the most Rust'y solution.
enum S {
A { val: i32, anotherAval: u32 },
B { val: i32, anotherBval: f32 },
}
impl S {
fn val(&self) -> i32 {
match self {
S::A => A.val,
S::B => B.val,
}
}
}

How do I use a BorrowMut supertrait to access struct fields in a trait default method?

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?

Why does Rust require generic type declarations after the "impl" keyword?

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
}
}

Resources