简体   繁体   中英

Rust: Shorten generic type bounds

Is there a way to shorten generic type bounds in rust? This is the mess I have to put on a lot of structs impls etc.:

pub struct IncomingClientMessageWithAddress<State, Msg>
    where State: AppState + Clone + serde::Serialize + serde::de::DeserializeOwned + std::marker::Unpin + 'static,
          Msg: AppEvent + Clone + serde::Serialize + serde::de::DeserializeOwned + std::marker::Unpin + 'static {
...

I am basically looking to do something like this (I know that below doesn't work for traits):

type MyStateAlias = AppState + Clone + serde::Serialize + serde::de::DeserializeOwned + std::marker::Unpin + 'static;
type MyEventAlias = AppEvent + Clone + serde::Serialize + serde::de::DeserializeOwned + std::marker::Unpin + 'static;


pub struct IncomingClientMessageWithAddress<State, Msg>
    where State: MyStateAlias,
          Msg: MyEventAlias {
...

I sometimes realized that all occurrences of a certain trait are associated with other traits. In your case, if AppState always occurs with Clone and Serialize , you could already require these on top of state:

trait AppState : Clone + Serialize {/*...*/}

If not, you could still define an auxiliary trait

trait AuxAppState: AppState + Clone + Serialize {/*...*/}

and require State: AuxAppState .

Then, to automatically derive AuxAppState you'd have to impl it for every type that also impl ements State , Clone and Serialize :

impl<T> AuxAppState for T where T: AppState + Clone + Serialize {}

Finally, defining and impl ementing the AuxAppState could possibly be done by a macro to save some keystrokes :

macro_rules! auxiliary_trait{
    ($traitname: ident, $($t:tt)*) => {
        trait $traitname : $($t)* {}
        impl<T> $traitname for T where T: $($t)* {}
    }
}

All of this can possibly be done one day with trait aliases .

Moreover, I started to require trait bounds only where they are actually needed. Eg in many cases, the struct -definition itself does not rely on the trait bounds, only the impl , so I started omitting them on the struct , only keeping them in the impl .

You could use new traits with blanket implementations for this:

use serde::Serialize;
use serde::de::DeserializeOwned;
use std::marker::Unpin;

pub trait AppState {}
pub trait AppEvent {}

pub trait StateTrait : AppState + Clone + Serialize + DeserializeOwned + Unpin + 'static {}
impl<T : AppState + Clone + Serialize + DeserializeOwned + Unpin + 'static> StateTrait for T {}
pub trait EventTrait : AppEvent + Clone + Serialize + DeserializeOwned + Unpin + 'static {}
impl<T : AppEvent + Clone + Serialize + DeserializeOwned + Unpin + 'static> EventTrait for T {}

pub struct IncomingClientMessageWithAddress<State, Msg>
    where State: StateTrait, Msg: EventTrait
{
    ... 
}

playground

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM