Strange behavior with struct update syntax

I'm trying to compute the hash of a struct using my trait, I've implemented everything correctly but I'm stuck on something.
The hash of a field-by-field filled struct is different from a partially filled struct and I don't know why.
Actually I know, the structs end up with different content even though the fields have exactly the same value during initialization.
Could someone help me by explaining the reason for this?

#[repr(C)]
struct C {
    a: u32,
    b: *const u32,
    c: u64,
}

impl Default for C {
    fn default() -> Self {
        Self {
            a: 0,
            b: std::ptr::null(),
            c: 0xfff,
        }
    }
}

pub trait MyHash {
    fn hash(&self) -> u64;
}

impl MyHash for C {
    fn hash(&self) -> u64 {
        let slice = unsafe {
            std::slice::from_raw_parts(self as *const Self as *const u8, size_of::<Self>())
        };
        println!("slice: {:?}", slice);
        println!("slice len: {:?}", slice.len());

        seahash::hash(slice)
    }
}
    println!(
        "hash: {}",
        &C {
            a: 0x0,
            b: std::ptr::null(),
            c: 0xaaaa_bbbb_cccc_dddd,
        }
        .hash()
    );

    println!(
        "hash: {}",
        C {
            b: std::ptr::null(),
            c: 0xaaaa_bbbb_cccc_dddd,
            ..C::default()
        }
        .hash()
    );

Output:

slice: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 221, 221, 204, 204, 187, 187, 170, 170]
slice len: 24
hash: 8417598556897746283
slice: [0, 0, 0, 0, 237, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 221, 221, 204, 204, 187, 187, 170, 170]
slice len: 24
hash: 9656407462385924903

Your struct C has padding bytes. Reading those is undefined behavior.

7 Likes

This explains why it sometimes generated the hash correctly.
Thanks for the answer.

After solving the UB, are you certain you want to hash the address stored in b instead of hashing the data b points to?

The compiler creates a temporary reference in the second case and the slice is created correctly.

This creates a slice which includes fields a, b, c, and padding. b is an address, which is unusual to include in a hash. The slice doesn't include the data that b points to.

Got it but this is fine for my use case.
This is just a representation of real structures. If they have pointers other than null, the function that generates the hash will return None. Rarely will the implementation traverse the pointers to produce the hash of the data pointed to by them.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.