Open ia0 opened 1 month ago
Interesting. Could you provide a small toy example to illustrate the problem? (Note: I am not a maintainer but write a lot of tests that use serde. At the moment I can't imagine a realistic use case. A toy example would potentially open my eyes to a range of possibilities I hadn't considered before.)
I'm implementing an RPC protocol from a host to a device using an top-level enum whose discriminant indicates the RPC function:
enum Api<'a, T: Direction> {
Error(T::Type<'a, Error>),
PlatformVersion(T::Type<'a, PlatformVersion>),
RebootPlatform(T::Type<'a, RebootPlatform>),
UpdatePlatform<T::Type<'a, UpdatePlatform>),
InstallApplet<T::Type<'a, InstallApplet>),
// etc... but always Foo<T::Type<'a, Foo>)
}
This API is parametrized by the "direction" which is either Request
(host to device) or Response
(device to host):
pub trait Direction {
type Type<'a, T: Service>: Wire<'a>; // think of Wire<'a> as Serialize + Deserialize<'a>
}
pub trait Service {
type Request<'a>: Wire<'a>;
type Response<'a>: Wire<'a>;
}
pub enum Request {}
impl Direction for Request {
type Type<'a, T: Service> = T::Request<'a>;
}
pub enum Response {}
impl Direction for Response {
type Type<'a, T: Service> = T::Response<'a>;
}
The Error
variant is special, in the sense that the host should never send such request and the device is always allowed to reply with this variant instead of the same variant as the request, to indicate an error. To avoid mistakes, the Error
service is implemented using Infallible
for the request:
pub enum DeviceError {}
impl Service for DeviceError {
type Request<'a> = Infallible;
type Response<'a> = Error;
}
The code is still in progress. I'm still experimenting with it and in particular I decided to stop using serde and use my own Wire<'a>
trait for the following reasons:
#[cfg(feature = "host")]
to deprecate them, keeping new devices small, but letting the host talk with old devices).So I probably won't need this issue to be fixed for this particular problem, but I thought it might still be useful in general (i.e. anytime there is a generic enum, one of its variant may occasionally be Infallible
to disable it).
Interesting. Thank you for the details.
I want a compact and canonical format (like postcard, except for the canonical part).
Have you considered bincode? It is a postcard-like format used by e.g. google/tarpc
Have you considered bincode?
No I did not. Thanks for the link! From what I can see it has the same issues as serde:
In generic context (where there is a type parameter
T: Serialize + Deserialize<'a>
), it may sometimes happen that one wants to instantiate withInfaillible
(or some nested occurrence ofInfaillible
). It would be convenient ifserde
would provide such implementations.There is a simple work-around when the user doesn't need to use
Infaillible
directly and can use their ownImpossible
type:But this feels more like a work-around than a proper solution. In my opinion, a proper solution would be for
serde
to supportcore::convert::Infaillible
, as it does other standard types.Would this be something that could be supported?