How to make layout of struct linear?

It seems the layout of a struct is not linear. Looks like fields of struct in memory are not in the same order as they are written into the definition of the structure.
Is there a way to ask the compiler to preserve the order of fields?
What other solution of the problem can you suggest?
Any reading?

use std::borrow::Borrow;

fn main()
{
  let header = ObjectHeader
  {
    x : 1,
    h : 2,
    a : 3,
    f : 4,
    w : 5,
    body : (),
  };

  let object = header.form();
  let borrowed_header : &ObjectHeader = object.borrow();
  dbg!( borrowed_header );

}

//

#[ derive( Debug, Clone ) ]
pub struct ObjectGeneric< Body >
{
  pub x : i64,
  pub h : u32,
  pub a : u8,
  pub f : u16,
  pub w : u32,
  pub body : Body,
}

pub type ObjectHeader = ObjectGeneric< () >;

impl ObjectHeader
{
  pub fn form( self ) -> Object
  {
    let body = ObjectBody { j : 10 };
    Object
    {
      x : self.x,
      h : self.h,
      a : self.a,
      f : self.f,
      w : self.w,
      body,
    }
  }
}

#[ derive( Debug, Clone ) ]
pub struct ObjectBody
{
  pub j : u32,
}

pub type Object = ObjectGeneric< ObjectBody >;

//

impl Borrow< ObjectHeader > for Object
{
  fn borrow<'a>( &'a self ) -> &'a ObjectHeader
  {
    unsafe
    {
      dbg!( &self );
      let result = std::mem::transmute::< &'a Object, &'a ObjectHeader >( self );
      dbg!( &result );
      result
    }
  }
}

//

Output:

[src/main.rs:69] &self = ObjectGeneric {
    x: 1,
    h: 2,
    a: 3,
    f: 4,
    w: 5,
    body: ObjectBody {
        j: 10,
    },
}
[src/main.rs:71] &result = ObjectGeneric {
    x: 1,
    h: 2,
    a: 0,
    f: 10,
    w: 5,
    body: (),
}
[src/main.rs:17] borrowed_header = ObjectGeneric {
    x: 1,
    h: 2,
    a: 0,
    f: 10,
    w: 5,
    body: (),
}

Playground

Use #[repr(C)].

8 Likes

Thank you that works! What Rust do with structures by default?

IIRC it's unspecified and subject to change.
I think in practice it reorders fields to reduce padding

2 Likes

Very interesting! Is there algorithm described somewhere?

No where formally, since the point is to remain flexible. Currently the behavior is to minimize size by minimizing padding, is seems. I believe this can be achieved by sorting the fields in order of decreasing alignment. However, that is not guaranteed, and things like PGO may change the order.

(I didn't check the source code to confirm actual algorithm in use or if PGO can affect field ordering today. The important point is that you can't count on field order with the default layout. The algorithm and/or PGO's influence may change tomorrow, or even between compiler invocations.)

4 Likes

PGO can't affect field ordering in the current implementation. There is no way to tell LLVM that it is allowed to reorder fields right now AFAIK.

1 Like

But Rust reserves the right for a future Rust compiler to use PGO and knowledge of the target architecture to reorder fields to improve iCache performance, usually by minimizing code size on architectures that encode small field offsets more densely than larger field offsets. Note that this can result in the compiler ordering fields differently in two or more unrelated data structures in the program source that coincidentally have the same nominal source layout in terms of field size and alignment.

1 Like

Since https://github.com/rust-lang/rust/pull/87868 -Z randomize-layout can be used to randomize the layout of structs to detect code that incorrectly relies on the layout. It doesn't randomize in every allowed way yet though.

3 Likes

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.