rust-lang / rust-lang/rust-bindgen

Inconsistent behaviour of `derive_default` for opaque types

Open
#3,096 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Rust
Stars
5.3k
Forks
829
Avg merge
1d 1h
Merged PRs (30d)
15

Description

There seems to be inconsistent behavior for derive_default based on whether the type is detected to be opaque by bindgen or explicitly marked as opaque by bindgen.

For a C definition of struct Foo; for an opaque struct, bindgen will generate the following with derive_default(true):


#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct Foo {
    _unused: [u8; 0],
}

When Foo is also explicitly marked opaque with .opaque_type("Foo"), it generates the following:

#[repr(C)]
#[derive(Debug, Default, Copy, Clone)]
pub struct Foo {
    _unused: [u8; 0],
}

I don't see why explicitly marking the type as opaque should change whether or not Default is derived. I also think that it makes even less sense that Default gets derived for a type that is supposed to be Opaque, since opaque types should never be directly accessed and should be handled only via pointers.

Other Information
rust-bindgen 0.71.1
clang version 19.1.7
Target: x86_64-pc-windows-msvc

Full Code Sample
build.rs:

use std::env;
use std::path::PathBuf;

fn main() {
    tracing_subscriber::fmt::init();

    let builder = bindgen::Builder::default()
        .header_contents(
            "input.h",
            r#"
struct Foo;

struct BAR__{int unused;};
typedef struct BAR__ *BAR;
"#,
        )
        .parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
        .derive_default(true);

    let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());

    builder
        .clone()
        .generate()
        .expect("Unable to generate bindings")
        .write_to_file((out_path).join("not-explicit-opaque.rs"))
        .expect("Couldn't write bindings!");

    builder
        .opaque_type("Foo")
        .opaque_type("BAR__") // Only BAR__ should be marked as opaque. The convenience type BAR should remain as a pointer to the opaque BAR__. 
        .generate()
        .expect("Unable to generate bindings")
        .write_to_file((out_path).join("explicit-opaque.rs"))
        .expect("Couldn't write bindings!");
}

Output:
not-explicit-opaque.rs:

/* automatically generated by rust-bindgen 0.71.1 */

#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct Foo {
    _unused: [u8; 0],
}
#[repr(C)]
#[derive(Debug, Default, Copy, Clone)]
pub struct BAR__ {
    pub unused: ::std::os::raw::c_int,
}
#[allow(clippy::unnecessary_operation, clippy::identity_op)]
const _: () = {
    ["Size of BAR__"][::std::mem::size_of::<BAR__>() - 4usize];
    ["Alignment of BAR__"][::std::mem::align_of::<BAR__>() - 4usize];
    ["Offset of field: BAR__::unused"][::std::mem::offset_of!(BAR__, unused) - 0usize];
};
pub type BAR = *mut BAR__;

explicit-opaque.rs:

/* automatically generated by rust-bindgen 0.71.1 */

#[repr(C)]
#[derive(Debug, Default, Copy, Clone)]
pub struct Foo {
    _unused: [u8; 0],
}
#[repr(C)]
#[repr(align(4))]
#[derive(Debug, Default, Copy, Clone)]
pub struct BAR__ {
    pub _bindgen_opaque_blob: u32,
}
#[allow(clippy::unnecessary_operation, clippy::identity_op)]
const _: () = {
    ["Size of BAR__"][::std::mem::size_of::<BAR__>() - 4usize];
    ["Alignment of BAR__"][::std::mem::align_of::<BAR__>() - 4usize];
};
pub type BAR = *mut BAR__;

Output Diff (left is no explicit opaque. right is explicit opaque):
Image

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Reproduce the difference from build.rs using derive_default(true), opaque_type("Foo"), and opaque_type("BAR__"), then compare not-explicit-opaque.rs with explicit-opaque.rs. Trace how bindgen chooses derives for inferred versus explicitly opaque types, and make the generated Default behavior consistent with the intended opaque-type policy.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
devtools
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.