# Check for zero C struct memory

**URL:** <https://users.rust-lang.org/t/check-for-zero-c-struct-memory/33713>\
**Category:** help\
**Created:** [October 17, 2019, 3:04pm UTC](https://users.rust-lang.org/t/check-for-zero-c-struct-memory/33713 "2019-10-17T15:04:08Z")\
**Posts on this page:** 1\
**Showing post:** 21

<div class="post-metadata">

**Author:** ![Yandros](https://sea1.discourse-cdn.com/flex019/user_avatar/users.rust-lang.org/yandros/32/8136_2.png) [@Yandros](https://users.rust-lang.org/u/Yandros)\
**Post date:** [October 20, 2019, 12:46pm UTC](https://users.rust-lang.org/t/check-for-zero-c-struct-memory/33713/21 "2019-10-20T12:46:39Z")

</div>

So, there are two questions at hand here:

## How do we `memcmp` two "bags" of bytes (`[u8]`) to test for equality?

Easy, just use the `==` operator on these bytes: [`<[u8] as Eq>::eq()` does use `memcmp`](https://doc.rust-lang.org/1.38.0/src/core/slice/mod.rs.html#5420-5436).

## Can I see a struct as just a "bag of bytes"? If so, how?

Now, this is where things are subtle. The general answer is: "No, you cannot!"

Indeed, you can't go and feed a struct you know nothing about to the following function:

```rust
unsafe // Can be UB!
fn as_bag_of_bytes<T: ?Sized> (
    ptr: &'_ T,
) -> &'_ [u8]
{
    ::core::slice::from_raw_parts(
        // ptr
        ptr as *const T as *const u8,

        // len
        ::core::mem::size_of_val(ptr),
    )
}

```

The root cause of that being UB is that **`T` may have padding bytes** , as @Hyeonu said (I didn't know about the zero-initialized "exception" to the rule; anyways, since you won't be calling that function on a statically known zero-initialized struct, that exception in practice doesn't even count). So, if your struct has padding, you cannot call `as_bag_of_bytes()` on it.

That being said, it would be nice to have the above function for padding-less types, such as primitives or structs that have been carefully crafted to ensure they do not contain any padding.

And this is indeed possible:

1. Define a new `unsafe trait AsBytes` (or `NoPadding`), that we will implement for types that have no padding. This way the above generic function can have a `T : AsBytes` bound and no longer be marked `unsafe`!

2. `unsafe impl AsBytes` for:

3. Generate a `#[derive(AsBytes)]` procedural macro (`macro_rules!` macro for the playground) that checks:

And now you can just call `.as_bytes()` on valid types and you'll get a **zero-cost** `&[u8]`, that you can then `==` compare to get an efficienct `memcmp`!

```rust
derive_AsBytes! {
    #[repr(C)]
    struct Ok {
        a: u16,
        b: u8,
        c: u8,
    }
}

#[cfg(FALSE)] // Uncomment this line to get compilation errors
mod fails {
    derive_AsBytes! {
        #[repr(C)]
        struct InnerPadding {
            a: u8,
            // inner padding byte
            b: u16,
        }
    }
    
    derive_AsBytes! {
        #[repr(C)]
        struct TrailingPadding {
            a: u16,
            b: u8,
            // trailing padding byte
        }
    }
}

fn main ()
{
    dbg!(Ok { a: 10752, b: 27, c: 0 }.as_bytes());
}

```

- yields

- [Playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=80368ed6785eb0bc639a4d6c952c694a)

* * *

If that sounds like a tedious macro to write, and you think a crate should be exporting such functionality, then you are right! There already is such a crate, from which I've taken this idea:

- [`::zerocopy`](https://docs.rs/zerocopy/0.2.8/zerocopy/trait.AsBytes.html)

* * *

There is another way to avoid padding, and that's by adding a `#[repr(packed)]` attribute on a `struct`. However, this adds a whole can of worms / bugs on itself, since now all the reads and writes on the fields of the struct need to be `unaligned` reads/writes using raw pointers, which is quite error-prone and thus `unsafe`. The only way this solution is easy to do is when all its fields have an alignment of `1`, such as when using [`::zerocopy::byteorder`](https://docs.rs/zerocopy/0.2.8/zerocopy/byteorder/struct.I32.html) integer types. But in that case `#[repr(packed)]` is not doing anything, and we are back to a `#[repr(C)]` struct carefully crafted without padding bytes.

---

_[View the full topic](https://users.rust-lang.org/t/check-for-zero-c-struct-memory/33713)._
