Example struct that will be created and serialized often:
pub struct PriceMessage
{
pub message_type: String, // this will always be "price"
}
I want "message_type" to always equal e.g. "price" in order to save allocating a new string every time I create and serialize this struct using serde_json crate.
If you wanted to have a compile constant default string value for a field, that could at run time later be replaced by a heap allocated string, you could do something like this:
pub struct SomeStruct {
pub some_field: Cow<'static, str>,
}
impl SomeStruct {
const SOME_FIELD_DEFAULT: Cow<'static, str> = Cow::Borrowed("Foobar!");
pub fn new() -> Self {
Self {
some_field: Self::SOME_FIELD_DEFAULT,
}
}
}
However if you want to have an actually constant field, this doesn't really make much sense in rust, and you should consider just using an associated constant.
Related
I need to serialize a struct from a remote crate and all of the fields in the struct are private. There are getter's implemented in the remote struct to get those values. I am following this guidance and got it to work just fine for primitive types. However, I'm struggling with how to implement this for non-primitive types (ie: String) that the remote struct contains.
Below is a small piece of what I've implemented to frame the issue. The DataWrapper struct simply wraps Data, where Data is the remote struct.
#[derive(Serialize)]
pub struct DataWrapper {
#[serde(with = "DataDef")]
pub data: Data,
}
#[derive(Serialize)]
#[serde(remote = "remote_crate::data::Data")]
pub struct DataDef {
#[serde(getter = "Data::image_id")] // This works
image_id: u16,
#[serde(getter = "Data::description")] // This does not work
description: String,
}
The error I get when compiling this is
#[derive(Serialize)]
^^^^^^^^^ expected struct `std::string::String`, found `&str`
This makes sense, since the getter Data::description returns &str rather than a String. But, I'm not seeing a way in my code to coerce this so the compiler is happy.
If I change DataDef::description to be &str instead of String, then I have to implement lifetimes. But, when I do that, the compiler then says the remote "struct takes 0 lifetime arguments".
Appreciate any tips on how I can serialize this and other non-primitive types.
One approach you could do, so that you have full control of the serialization. Is to have the data wrapper be a copy of the struct fields you need, instead of the entire remote struct. Then you can implement From<remote_crate::data::Data> for DataWrapper and use serde without trying to coerce types.
#[derive(Serialize)]
pub struct Data {
image_id: u16,
description: String,
}
impl From<remote_crate::data::Data> for Data {
fn from(val: remote_crate::data::Data) -> Self {
Self {
image_id: val.image_id,
description: val.description.to_string(),
}
}
}
// Then you could use it like this:
// let my_data: Data = data.into();
I couldn't get it to work with an &str, but if you're OK with an allocation, you can write a custom getter:
mod local {
use super::remote::RemoteData;
use serde::{Deserialize, Serialize};
fn get_owned_description(rd: &RemoteData) -> String {
rd.description().into()
}
#[derive(Serialize)]
#[serde(remote = "RemoteData")]
pub struct DataDef {
#[serde(getter = "get_owned_description")]
description: String,
}
}
mod remote {
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
pub struct RemoteData {
description: String,
}
impl RemoteData {
pub fn description(&self) -> &str {
&self.description
}
}
}
https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=e9c7c0b069d7e16b6faac2fa2b840c72
I am basically following the gtk-rs book to create a simple app. I have the following rust struct, which I want to display in a ListView. This struct is basically read-only and should not be edited, so I do not want to expose it's members as properties and I also do not want to create it from GTK itself (it's created inside the rust-only business logic). I would like to expose the entire struct as one property.
pub struct Video {
pub number: u32,
pub encoding: Encoding,
pub chapters: Vec<Chapter>,
}
pub struct Chapter {
pub number: u32,
pub filename: String,
pub thumbnail_filename: Option<String>,
pub preview_video_filename: Option<String>,
}
How can I wrap this inside a GObject? I'm trying to create the following wrapper:
mod imp {
#[derive(Default)]
pub struct VideoObject {
pub data: Rc<RefCell<Video>>,
}
#[glib::object_subclass]
impl ObjectSubclass for VideoObject {
const NAME: &'static str = "VideoObject";
type Type = super::VideoObject;
}
impl ObjectImpl for VideoObject {}
}
pub const VIDEO_PROPERTY: &str = "video";
glib::wrapper! {
pub struct VideoObject(ObjectSubclass<imp::VideoObject>);
}
impl VideoObject {
pub fn new(video: Video) -> Self {
Object::new(&[]).expect("Could not create VideoObject")
}
}
This fails because Video does not implement Default. I thought about wrapping it inside an Option like data: Rc<RefCell<Option<Video>>>, which compiles. Unfortunately, then I'm stuck on how to set it as a property.
I guess one way would be to use a ParamSpecPointer, box and leak Video and then pass it before, but this strucks me as unsafe and ugly...
Is there a better way to do it?
You can directly access the "imp" object and set fields manually. No need to transform everything into GObject properties:
impl VideoObject {
pub fn new(video: Video) -> Self {
let object = Object::new(&[]).unwrap();
let imp = imp::VideoObject::from_instance(&object);
imp.data.replace(Some(video));
object
}
}
I need to serialize some data into files. For the sake of memory efficiency, I want to use the default compact serializer of MessagePack (MsgPack), as it only serializes field values w/o their names. I also want to be able to make changes to the data structure in future versions, which obviously can't be done w/o also storing some meta/versioning information. I imagine the most efficient way to do it is to simply use some "header" field for that purpose. Here is an example:
pub struct Data {
pub version: u8,
pub items: Vec<Item>,
}
pub struct Item {
pub field_a: i32,
pub field_b: String,
pub field_c: i16, // Added in version 3
}
Can I do something like that in rmp-serde (or maybe some other crate?) - to somehow annotate that a certain struct field should only be taken into account for specific file versions?
You can achieve this by writing a custom deserializer like this:
use serde::de::Error;
use serde::{Deserialize, Deserializer, Serialize};
#[derive(Serialize)]
pub struct Data {
pub version: u8,
pub items: Vec<Item>,
}
#[derive(Serialize)]
pub struct Item {
pub field_a: i32,
pub field_b: String,
pub field_c: i16, // Added in version 3
}
impl<'de> Deserialize<'de> for Data {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
// Inner structs, used for deserializing only
#[derive(Deserialize)]
pub struct InnerData {
version: u8,
items: Vec<InnerItem>,
}
#[derive(Deserialize)]
pub struct InnerItem {
field_a: i32,
field_b: String,
field_c: Option<i16>, // Added in version 3 - note that this field is optional
}
// Deserializer the inner structs
let inner_data = InnerData::deserialize(deserializer)?;
// Get the version so we can add custom logic based on the version later on
let version = inner_data.version;
// Map the InnerData/InnerItem structs to Data/Item using our version based logic
Ok(Data {
version,
items: inner_data
.items
.into_iter()
.map(|item| {
Ok(Item {
field_a: item.field_a,
field_b: item.field_b,
field_c: if version < 3 {
42 // Default value
} else {
// Get the value of field_c
// If it's missing return an error, since it's required since version 3
// Otherwise return the value
item.field_c
.map_or(Err(D::Error::missing_field("field_c")), Ok)?
},
})
})
.collect::<Result<_, _>>()?,
})
}
}
Short explanation how the deserializer works:
We create a "dumb" inner struct which is a copy of your structs but the "new" fields are optional
We deserialize to the new inner structs
We map from our inner to our outer structs using version-based logic
If one of the new fields is missing in a new version we return a D::Error::missing_field error
The question pretty much says it: can I create a boxed tuple (or any struct) that directly contains a slice as one of its fields?
The Vec::into_boxed_slice method works to create Box<[U]>, but I would like to add on some additional data directly in the box alongside the slice.
For example, here is the kind of struct I would like to box up without using Vec into something like Box<(i32,[u8])>:
struct ConsolidateMe {
data: i32,
arr: Vec<u8>,
}
impl ConsolidateMe {
fn new(n: usize) -> Self {
let mut arr = Vec::with_capacity(n);
arr.resize(n, 42);
ConsolidateMe {
data: 15,
arr,
}
}
}
(In my application, I am creating many structs on the heap, each of which contains a small array and a few other values. The arrays have different sizes, but the size doesn't change after the struct is created. I'd like these to be compactly stored and to avoid extra indirection to the extent possible.)
The slice-dst crate looks like a perfect fit.
Example:
use slice_dst::SliceWithHeader;
#[derive(Debug, Clone)]
struct MyType {
inner: Box<SliceWithHeader<String, i32>>,
}
impl MyType {
fn new(value: &str) -> Self {
let values: Vec<i32> = value.split(" ").map(|s| s.trim().parse().unwrap()).collect();
Self {
inner: SliceWithHeader::new(value.into(), values.into_iter()),
}
}
}
fn main() {
println!("{:?}", MyType::new("1 2 3"));
}
I'm trying to figure out how to add a field that is a cryptographic hash of other fields. I have:
pub struct Message {
pub size: usize,
pub id: MessageId,
pub attribute: MessageAttribute,
}
I'd like to have something like:
pub struct Message {
pub size: usize,
pub id: MessageId,
pub attribute: MessageAttribute,
pub hash: MessageHash,
}
pub struct MessageHash(pub Vec<u8>);
I could create another struct and then compute the hash when setting up the struct:
pub struct HashedMessage {
pub content: Message,
pub hash: MessageHash,
}
pub fn message_hash(data: &Message) -> MessageHash {
let mut hasher = DefaultHasher::new();
data.hash(&mut hasher);
MessageHash(hasher.finalize().to_vec())
}
let content = Message { /* ... */ };
let hash = hash_message(msg);
let msg = HashedMessage { content, hash };
This method introduces another struct, and I would have to change the codebase to use the new struct in place of the old one.
Another way I is to have a new method that receives each member of the struct, and then output the final struct with the hash field computed over the inputs. This seems reasonable. A possible option would be to set the hash to 0 in this method and then compute the hash using message_hash over the Message struct (the first one, with hash field embedded), and then assign it.
Is there another way? What would be an idiomatic, correct way to add a hash? These are messages that will be serialized and sent over the wire, so I can't have a method that will compute the hash every time.
You can use composition like this:
pub struct Message {
pub size: usize,
pub id: MessageId,
pub attribute: MessageAttribute,
}
pub struct Hashed<M> {
pub hash: MessageHash,
pub message: M,
}
impl<M> Hashed<M> {
fn new(message: M) -> Self {
let hash = compute_hash(&message);
Self {
hash,
message,
}
}
}
Then you can use either Message or Hashed<Message>.
If you have a Hashed<Message> but you need a Message, you can borrow that field. Going the other way, you'd have to create a new Hashed<Message> and compute the hash again.