prost icon indicating copy to clipboard operation
prost copied to clipboard

Allow ignoring fields

Open mashedcode opened this issue 6 years ago • 6 comments
trafficstars

If one has a structure which carries data and temporary state. One might want to encode only the data and ignore the rest.

It seems to me that the simplest way to implement this would be to simply ignore fields which are not annotated. As part of this I'd also stop generating impl Default since a user may very well want to implement it himself to initialize the ignored fields correctly. For generated code a simple #[derive(Default should do the trick IIUC. Either way this would be a breaking change.

Suggestions?

I'd be happy to implement.

mashedcode avatar Apr 04 '19 10:04 mashedcode

I really need this

fanhaining avatar Feb 07 '20 10:02 fanhaining

I also found myself needing this. I have a existing very large struct and I don't want to clone it only to drop a field...

DontBreakAlex avatar Oct 13 '22 15:10 DontBreakAlex

I don't understand what you are trying to do. Can you provide an reproducible example?

caspermeijn avatar Aug 29 '24 09:08 caspermeijn

I do not think this applies to types that use *.proto files because they simply would not be code-generated. I have a use case where I want to model a message and ProtoBuf just happens to be the serialization format I want to use. I don't what to have to create DTOs or other intermediate structs just to drop the unwanted field.

Example:

#[derive(Message)]
struct Event {
    #[prost(uint32)]
    id: u32,

    #[prost(skip)]
    version: u32,

   /// ... other fields
}

I have no interest in storing version because it's irrelevant to the encoded message. I need and store that information outside of the struct; however, it's a lot of extra work and ceremony to wrap that behavior over another struct or provide a conversion struct. Similarly, having version: Option<u32> and setting it to None isn't much help. It's still a field in ProtoBuf's eyes.

It doesn't really matter what it's called. serde uses #[serde(skip)] so this would be congruent, but #[prost(ignore)] is just as meaningful IMHO. This problem can be exacerbated by a field that is much larger in size. There should be some way to ignore such a field. Since every field used this way requires an annotation, I'm also onboard with simply ignoring any field that doesn't have an annotation.

commonsensesoftware avatar Nov 15 '24 01:11 commonsensesoftware

What do you expect the value to be when such a struct is deserialized?

Having a skip attribute makes sense to me. It needs someone to actually implement this.

caspermeijn avatar Nov 20 '24 08:11 caspermeijn

I'm open to possibilities, but I'd imagine rules similar to serde.

  1. The field must support Default, which works for any primitive and recursively, in particular, for Option
  2. Support wiring up a function which allow providing a default value, which cannot otherwise be idiomatically provided (ex: #[prost(skip, default_value="custom_init_fn")])

Revising the setup, it might be something more contrived like:

fn get_answer() -> u32 {
    42
}

#[derive(Message)]
struct Event {
    #[prost(uint32)]
    id: u32,

    #[prost(skip)]
    version: u32,

    #[prost(skip, default_value = "get_answer")]
    answer_to_the_universe: u32,

    #[prost(skip)]
    answer: Option<String>,

   /// ... other fields
}

The implementation macro would expand struct initialization to something like:

let mut message = Event {
    id: Default::default(),
    version: Default::default(),
    answer_to_the_universe: get_answer(),
    answer: Default::default()
}

At this point, things can resume down the Message::merge_field path, which would ignore fields that are skipped. I think that's already covered because there won't be any tags for those fields in the message.

commonsensesoftware avatar Nov 20 '24 14:11 commonsensesoftware