Solve `std::os::raw::c_void`

I guess it’s a bit silly, but c_void could alone go into libcore.

I think c_void and c_char should be in core. The fact that compilers allow for changing the signedness of char via a commandline flag isn’t really an issue unique to Rust. The platform has to pick a default for compiled code and overriding that default will cause the same incompatibilities with existing C code as it would with existing Rust code.

As for the other types, I would initially be conservative and put them in a ctypes crate between core and std.

Many of the strategies we’ve been discussing recently involve putting the definition of c_void “under” std, either via core or a new ctypes crate. Then both std and libc could use this shared definition.

In the initial post, @bluss claimed that repointing libc to a shared definition like that would require a major version bump in the crate. (There was a further claim that a major bump will eventually be needed for one reason or another, but let’s put that aside for the moment.)

Questions:

  • Is such a major version bump strictly necessary? Why?
  • If we have to do a major bump to libc, isn’t that likely to cause at least as much pain as deprecating std::os::raw in favor of libc would?

In my opinion, changing libc::c_void to be a reexport of a c_void from a higher up crate would not be a breaking change. I can’t imagine how any code could rely on the assumption that the type isn’t a re-export and be broken by that change.

  • My idea was that libc 0.2.16 and libc 0.2.17 need to have compatible c_void definitions. That seems to be a misunderstanding actually, since you wouldn’t use both of those in the same project (and if you do, their c_voids are incompatible anyway).
  • A major bump in libc is planned anyway (1.0), so that’s not an issue.

Thanks @retep998 and @bluss!

So with that out of the way, I feel like @alexcrichton’s arguments against putting types in core don’t apply to c_void, which happens to be the only type for which the location matters.

Thus, if we want to solve this problem quickly and with minimal pain and commitment, the most plausible routes seem to be one of the following:

  1. Introducing a ctypes crate under std with all of std::os::raw in it.
  2. Adding c_void alone to core, which doesn’t require weighing in on various C issues but solves the main problem.

If we are motivated to remove std::os::raw from std (perhaps for the reasons @alexcrichton laid out), I think the ctypes approach is the best solution. We could take the following steps:

  • Introduce the ctypes crate into the Rust distro, and make std re-export its definitions in std::os::raw.
  • Update libc to also re-export those types, with a minor version bump.
    • Note: this is a bit tricky since it requires a particular version of the Rust distro to work; we’d probably need some way to deal with that, either via detection or feature flags.
  • Now, the world of libc and std::os::raw types are fully compatible, solving the original problem.
  • At this point, we can safely deprecate std::os::raw in favor of ctypes, and perhaps at some later date remove those deprecations in a major version of Rust.
    • This deprecation no longer causes massive pain, because you can switch to the libc (or ctypes) definitions without breakage.

Thoughts?

2 Likes

Overall that plan seems reasonable to me, but diving into the details on this one may show that it's pretty tricky. We could actually switch these to reexports today, except that libc now has a feature to support #![no_std]. I would feel pretty bad about reexporting if you link to std and defining if you don't, and it also seems like it could cause confusion/breakage.

That is, we may not be able to switch libc to reexport until the ctypes crate is stable, unless we're willing to define some of the time and reexport the rest of the time.

Are there any arguments against doing this?

I would personally avoid doing so as it seems “pretty weird” that sometimes libc defines these types and other times it doesn’t. There is indeed subtle breakage that can happen by defining a new type vs reexporting another. A silly and contrived example is impl LocalTrait for libc::c_void plus impl LocalTrait for std::os::raw::c_void, where that will compile with #![no_std] and it won’t compile if libc links to std.

That, and it still doesn’t solve the problem of “one source of truth” for these types (which is the original crux of this issue). If we do that then I would think that we’re basically where we are today, with the “pain” just shifted somewhere else.

I think it sounds good. A ctypes crate is more than c_void needs, but from the discussion here it sounds like the best solution.

Hm so another possible snag is that ideally libc never links to libstd. That is, the use_std feature just shouldn’t exist because the library never needs std (it was just left in for backwards compatibility in niche use cases).

2 Likes

@aturon @alexcrichton What’s needed to move forward on this?

@aturon @alexcrichton another ping now you’re back from your conference

I opened a RFC for a ctypes crate.

Ongoing discussion has moved onto that RFC.

@jethrogb, apologies for the long delay resolving this issue. It fell off my radar.

RFC https://github.com/rust-lang/rfcs/pull/1783 was eventually closed/postponed.

New RFC to propose fixing this through core::ffi::c_void: https://github.com/rust-lang/rfcs/pull/2521

2 Likes

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.