Skip to content
tezvyn:
Menu

Why must FFI-bound structs use #[repr(C)] and what breaks without it?

Source: doc.rust-lang.orgMediumHow cards are made

Summary

Rust ABI stability across FFI.

Key points

repr(C) fixes field order, size, alignment to C rules for extern calls; omitting it lets Rust reorder or pad fields, causing UB.

Watch out for

Believing default layout is stable or repr(C) is optional.

What's really being asked

This question tests whether you understand that Rust's default struct layout is not stabilized and that FFI requires a predictable ABI. The interviewer wants to see that you know repr(C) is not decorative but a semantic requirement for sound interoperability with C code. They are also checking if you recognize that layout mismatches cause undefined behavior rather than mere performance penalties.

The full answer

A strong answer starts by stating that repr(C) instructs the compiler to lay out the struct exactly as C or C++ would, fixing field order, size, and alignment. Next, it explains that omitting it leaves the layout unspecified, meaning the compiler may reorder fields, insert padding differently than C expects, or choose an alignment that does not match the foreign side. Third, it notes that passing a default-layout Rust struct across an extern boundary leads to the foreign code reading garbage from wrong offsets, which is immediate undefined behavior. Finally, a senior candidate mentions edge cases: zero-sized types remain zero-sized under repr(C) even though C++ empty structs consume one byte, wide pointers and tuples are never FFI-safe even with repr(C), and enums with fields require special bridging.

The mistakes people make

A red flag is claiming that Rust's default layout is stable or deterministic enough for FFI. Another is saying repr(C) is only for performance or cache locality. Some candidates confuse repr(C) with repr(transparent), arguing that a single-field wrapper is enough; while repr(transparent) is useful for newtypes, it is not the general solution for multi-field structs. Finally, suggesting that you can safely transmute or pointer-cast a default-layout Rust struct to a C struct shows a dangerous misunderstanding of the language guarantees.

What usually comes next

The interviewer may ask how you would handle a struct containing a String or Vec across FFI, which should be answered by converting to raw pointers or FFI-safe types before crossing the boundary. They might ask about repr(u8) on fieldless enums, or how to guarantee layout for tagged unions. Another follow-up is whether repr(C) alone makes a type FFI-safe, prompting a discussion of DSTs, tuples, and ZSTs.

A concrete example

Imagine a Rust struct Point { x: u32, y: u8 } without repr(C). The compiler might reorder fields or add trailing padding so the size becomes 8 bytes with y at offset 4. On the C side, a struct with the same declaration could place x at offset 0, y at offset 4, and have size 8, which happens to match on some targets but is not guaranteed. If Rust instead inlines or reorders y before x, the C code reading pointer->x would actually read the y byte plus three bytes of padding, corrupting the value. Adding repr(C) locks the layout to the platform C ABI, making the mapping sound.

Interview question

Which statement best explains why omitting #[repr(C)] on an FFI-bound struct leads to undefined behavior?

  • a.The call will not compile unless the struct is wrapped in repr(transparent)
  • b.Rust may reorder fields or pad the struct differently than the C side expectsCorrect
  • c.On most tier-1 platforms the default layout already matches C, so UB is rare in practice
  • d.Field access becomes slower because Rust cannot apply layout optimizations
Why?

Without repr(B), Rust's default struct layout is unspecified, so the compiler may reorder fields or insert padding that does not match the C side, causing the foreign code to read garbage and triggering immediate undefined behavior. Distractor A is a common and dangerous misconception because Rust never guarantees its default layout matches C, even on tier-1 targets.

Just read this? Test yourself on what you have been reading.

Read the original → doc.rust-lang.org

Put your scrolling time to good use

Learn one idea, try a quiz and save useful cards for revision. Tezvyn makes it easy to learn and stay current in your tech field, a few minutes at a time.

The iPhone app is on the way

We are building it. Until it lands, nothing here is held back from you: every interview card, your saved cards, streaks and the job board all work in Safari, plus hundreds of free practice quizzes of thirty questions each. Sign in and it all carries over to the app the day it arrives.

Want it as an icon? Tap Share at the bottom of Safari, then Add to Home Screen. It opens full screen and the cards you have read stay available offline.

Get it on Google PlayiPhone app coming soon

We are hiring for this. Open roles that interview on rust — each one lists the topics its interview covers.

See open roles