Updating one field of struct w/o listing all other fields ?//

#1
  1. Consider
mod go;


pub struct Foo<T> {
    pub a: i32,
    pub b: i32,
    pub c: i32,
    pub x: i32,
    pub y: u32,
    pub z: f32,
    pub t: T,
}

pub struct Cat{}
pub struct Dog{}

pub fn magic(foo: &Foo<Cat>) -> Foo<Dog> {
    let Foo{a, b, c, x, y, z, t} = foo;
    Foo{a: *a,
        b: *b,
        c: *c,
        x: *x ,
        y: *y,
        z: *z,
        t: Dog{} }
}

#[test]
pub fn test_00() {

}
  1. I want to copy the object + modify one field.

  2. Is there a short hand to do this without deconstructing all the fields + rebuilding it? It’s same to assume that all fields either: has clone, has copy, or is ref.

#2

I think you’re looking for the .. operator.

pub fn magic(foo: &Foo<Cat>) -> Foo<Dog> {
   Foo { t: Dog {}, ..*foo }
}

Edit: This won’t work in your case because <T> is different.

2 Likes
#3

Yeah, I hit this kind of thing often when impl From<A> for B where B is an “updated” or “fixed” version of A, for example A is what serde parses from a raw log, and B has done some extra validation or normalisation.

You’d think I’d remember, but I keep tripping over this. Which tells me it would be nice to be able to do, somehow.

1 Like
#4

You could use unsafe code to do this with std::mem::transmute but getting the struct sizes right wouldn’t be fun. I’m messing around in a Playground right now to see if I can be creative. :slight_smile:

#5

..*foo is good to know even if it doesn’t solve the problem.

#6

Even with the same size, the layouts may be different if they’re not #[repr(C)], breaking transmute.

You could group the common fields into some inner struct, so there’s just one big field to copy.

4 Likes
#7

Yeah, and this is great in combination with #[serde(flatten)], but only sometimes.

#8

You can do it if you remove the type parameter and switch to trait objects, but it’s a bit of a pain:

use std::any::Any;

struct Foo<'a> {
    a: i32,
    b: i32,
    c: i32,
    d: &'a dyn Any
}

struct Cat {
    a: i32
}

struct Dog {
    a: i32,
    b: i32
}

fn magic<'a>(input: &'a Foo<'a>) -> Foo<'a> {
    Foo { d: &Dog { a: 0, b: 0 }, ..*input }
}

(this is why having single structural inheritance would be great)

#9

In this specific example, you could also use an Animal enum with Cat and Dog variants in field f - but that’s not an answer to the general question.

#10

In your case it probably makes sense to move repeated fields into a separate structure:

#[derive(Clone)]
pub struct A {
    pub a: i32,
    pub b: i32,
    pub c: i32,
    pub x: i32,
    pub y: u32,
    pub z: f32,
}

pub struct Foo<T> {
    pub a: A,
    pub t: T,
}

Then the operation is simple:

pub fn magic(foo: &Foo<Cat>) -> Foo<Dog> {
    Foo {
        a: foo.a.clone(),
        t: Dog {},
    }
}
2 Likes
#11

An extension for FRU to support changing generic types has been submitted as an RFC:

Still a long way from being stable even if it is accepted, but would exactly solve the issue you’re having.

6 Likes