Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.
Sign upextern types #1861
Conversation
This comment has been minimized.
This comment has been minimized.
|
Given that this is declaring a new type and not an alias to an existing type (presumably you should be allowed to write |
nagisa
reviewed
Jan 21, 2017
| Add a new kind of type declaration, an `extern type`: | ||
| extern type Foo; |
This comment has been minimized.
This comment has been minimized.
nagisa
Jan 21, 2017
•
Contributor
This diverges somewhat from extern fn where its a definition and not declaration. For consistency IMHO declarations should stay constrained to extern blocks, like below.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
What do |
This comment has been minimized.
This comment has been minimized.
|
@petrochenkov it's a DST, you mean |
Ericson2314
added some commits
Jan 21, 2017
This comment has been minimized.
This comment has been minimized.
|
Oh, right, |
This comment has been minimized.
This comment has been minimized.
|
@petrochenkov I think will need to change #1524 (comment). I had a bunch of things to add to this RFC so I'm making a PR-on-PR; I propose making it panic for stop-gap. |
Ericson2314
added some commits
Jan 21, 2017
This comment has been minimized.
This comment has been minimized.
|
It's sort of like a "truely" abstract type, so I think |
This comment has been minimized.
This comment has been minimized.
|
But it's a nominal type that's knowably disjoint from all other types, isn't it? |
This comment has been minimized.
This comment has been minimized.
|
I think the fact that it is, after all, called a "struct" in C tips me over the fence into @glaebhoerl's camp. Yes, this is used as a hack around not having real abstract types in C [and there should be a way to make the size of a concrete struct with private fields abstract in Rust!], but I'm OK with skeuomorphisms for FFI. |
This comment has been minimized.
This comment has been minimized.
|
I may be a struct in C, or it may be a union, or it may be just a
I wonder why would you need to do such a thing. |
canndrew
added some commits
Jan 21, 2017
This comment has been minimized.
This comment has been minimized.
|
Thanks @Ericson2314 for some much-needed extra material. |
This comment has been minimized.
This comment has been minimized.
The fact they can only appear in I'd feel a little weird calling them structs when the type might not actually be a struct. That feels like a bit of a C-anachronism. |
This comment has been minimized.
This comment has been minimized.
There's a lot of object-oriented C APIs that work with pointers to some opaque type. You could wrap these APIs and expose safe versions of the C functions as methods on the extern type. |
This comment has been minimized.
This comment has been minimized.
I think it'd be fine to just allow I feel that the difference between whether it's an alias or a fresh type is much more significant than what variety of fresh type it is (especially given that the representation is hidden so that the difference is academic). |
This comment has been minimized.
This comment has been minimized.
|
I really want a feature like this but have a couple of concerns about the semantics of this proposal:
|
This comment has been minimized.
This comment has been minimized.
|
An alternative is to drop the
Posted separately to allow separate reactions (I like this alternative more then the proposal but I'm not entirely sure about it). |
This comment has been minimized.
This comment has been minimized.
briansmith
commented
Jan 22, 2017
|
The main issue I see with
The RFC should be updated to specifically mention that references to such types are allowed. It should also mention that moves from these types are not allowed. |
briansmith
reviewed
Jan 22, 2017
| struct FfiStruct { | ||
| data: u8, | ||
| more_data: u32, | ||
| tail: OpaqueTail, |
This comment has been minimized.
This comment has been minimized.
briansmith
Jan 22, 2017
It's not clear whether you are proposing that this should be accepted by the compiler or not. I think that this is not as essential as the rest of the proposal and I'd suggest that you remove this example and specify that such types can only be used as pointer and reference types.
This comment has been minimized.
This comment has been minimized.
canndrew
Jan 22, 2017
Author
Contributor
I'm saying it should be accepted. This isn't really hard to implement - just make extern types effect the struct pointer the same way that slices and trait objects do. It's also consistent with allowing other DSTs at the end of structs. I think it should be accepted because it's a pattern I've seen a lot in C code.
This comment has been minimized.
This comment has been minimized.
nagisa
Feb 14, 2017
•
Contributor
This structure seems useless to me. Consider 3 cases where FfiStruct could be used:
fn by_return() -> FfiStruct;
fn by_sret(retptr: *mut FfiStruct);
fn by_argument(arg: FfiStruct);
Now, all of these are invalid or can’t be made work:
- For
by_returnit may under covers be either a return by value or by sret pointer (analysed in next point); If its returned by value its all right. However you cannot really know if its by value or by sret without knowing the full defn' of structure; - For
by_sretcompiler simply cannot how much of stack space to allocate for the retptr slot; - For
by_argumentcompiler cannot properly how to correctly pass such a structure to the C side (i.e. if theFfiStructwas supposed to be passed to C via registers, how many registers does the C side expect to be used?).
EDIT: only case where this could work is
fn double_indirection(retptr: *mut *mut FfiStruct)
This comment has been minimized.
This comment has been minimized.
mystor
Feb 14, 2017
What about:
fn return_reference(&T) -> &FfiStruct {}
fn consume_reference(&FfiStruct) {}One of the main uses of this type would be to define opaque types with headers of known layouts such that they can be handled and have lifetimes managed as though they are Rust types, despite being defined in C++ land.
As an example, in the gecko codebase there is a type nsAString, which represents an abstract string. This type is an abstract base class which has (approximately) the following layout:
struct nsAString {
const uint16_t* data;
uint32_t length;
uint32_t flags;
};And has multiple subclasses, which may or may not add extra data after the above data, such as nsFixedString which has a layout like:
struct nsFixedString : public nsAString {
uint32_t capacity;
uint16_t* buffer;
};In rust we can then define nsAString as:
#[repr(C)]
struct nsAString {
data: *const u16,
length: u32,
flags: u32,
_rest: OpaqueTail
}And then we could take *mut nsAString, *const nsAString, *const nsFixedString etc. and cast them (through unsafe code) into &'a nsAString, working with them as though they were a rust object, able to directly access members from rust like length and flags, without having to worry about accidentally moving the data inside and breaking C++-defined invariants.
Currently in our rust bindings we're working around this limitation by defining #[repr(C)] struct nsAString([u8;0]); and doing casts to extract the fields from the header. You can see this here: http://searchfox.org/mozilla-central/rev/d3307f19d5dac31d7d36fc206b00b686de82eee4/xpcom/rust/nsstring/src/lib.rs#160-163
This comment has been minimized.
This comment has been minimized.
nagisa
Feb 14, 2017
Contributor
fn return_reference(&T) -> &FfiStruct {}
This can work, but you’ll need to either know the full type of T or get it on Rust side with *mut *mut T in the first place, basically moving the responsibility from FfiStruct directly to T.
Where’s the point of having a reference to
#[repr(C)]
struct nsAString {
data: *const u16,
length: u32,
flags: u32,
_rest: OpaqueTail
}over a reference to
#[repr(C)]
struct nsAString {
data: *const u16,
length: u32,
flags: u32,
}
So still, I’m not seeing the point of allowing such a thing, given in how many cases this cannot reasonably work in a FFI context.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
canndrew
Feb 16, 2017
Author
Contributor
@nagisa
by_sret is intended to work. extern type pointers are "fat" pointers in the sense that they point to a DST but they're still pointer-sized (the extra metadata on the pointer is just a ()). So *mut FfiStruct is pointer-sized and ffi-safe.
This comment has been minimized.
This comment has been minimized.
nagisa
Feb 16, 2017
Contributor
@canndrew How? Namely, please describe how much memory would the caller need to allocate on the stack so it could produce a valid pointer to pass to the function?
@Ericson2314 yes, I’ve seen it.
This comment has been minimized.
This comment has been minimized.
eternaleye
Feb 16, 2017
•
@nagisa: The entire point of this RFC is that no Rust code can possibly create such a value on the stack - it can only obtain pointers to such values, and only from foreign (or unsafe pointer-casting) code.
It is meant almost exactly to mimic the C/C++ notion of an "incomplete type" - which cannot be allocated on the stack in C/C++ either, but can be pointed/referred to.
This comment has been minimized.
This comment has been minimized.
canndrew
Feb 16, 2017
•
Author
Contributor
@nagisa 32 bits on a 32 bit system, 64 bits on a 64 bit system? I'm not sure I understand the question.
Edit: Oh I get it. Yes, what @eternaleye said.
This comment has been minimized.
This comment has been minimized.
rfcbot
commented
Jul 12, 2017
|
@aturon proposal cancelled. |
rfcbot
removed
the
proposed-final-comment-period
label
Jul 12, 2017
This comment has been minimized.
This comment has been minimized.
|
@rfcbot fcp merge |
This comment has been minimized.
This comment has been minimized.
rfcbot
commented
Jul 12, 2017
•
|
Team member @aturon has proposed to merge this. The next step is review by the rest of the tagged teams: No concerns currently listed. Once these reviewers reach consensus, this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up! See this document for info about what commands tagged team members can give me. |
rfcbot
added
the
proposed-final-comment-period
label
Jul 12, 2017
This comment has been minimized.
This comment has been minimized.
|
@withoutboats, I checked off your box despite your earlier concern, since you're currently on vacation. I feel confident you'll agree that we can move this issue to block stabilization rather than the RFC. |
This comment has been minimized.
This comment has been minimized.
rfcbot
commented
Jul 12, 2017
|
|
rfcbot
added
final-comment-period
and removed
proposed-final-comment-period
labels
Jul 12, 2017
This comment has been minimized.
This comment has been minimized.
rfcbot
commented
Jul 22, 2017
|
The final comment period is now complete. |
This comment has been minimized.
This comment has been minimized.
Saying that it is not 'fat pointer' is more clear for me. |
This comment has been minimized.
This comment has been minimized.
|
@aturon So we're sweet to merge this baby yeah? |
retep998
reviewed
Jul 24, 2017
| @@ -64,7 +64,7 @@ These types are FFI-safe. They are also DSTs, meaning that they do not implement | |||
| In Rust, pointers to DSTs carry metadata about the object being pointed to. | |||
| For strings and slices this is the length of the buffer, for trait objects this is the object's vtable. | |||
| For extern types the metadata is simply `()`. | |||
| This means that a pointer to an extern type is identical to a raw pointer. | |||
| This means that a pointer to an extern type is identical to a raw pointer (ie. it is not a "fat pointer"). | |||
This comment has been minimized.
This comment has been minimized.
retep998
Jul 24, 2017
Member
Probably better to just say it is not a fat pointer and not even bother bringing up raw pointers because raw pointers can be fat too! *const [T] is fat for example despite being a raw pointer.
aturon
referenced this pull request
Jul 25, 2017
Open
Tracking issue for RFC 1861: Extern types #43467
This comment has been minimized.
This comment has been minimized.
|
Huzzah! This RFC has been merged! Tracking issue. |
aturon
merged commit 4a27a85
into
rust-lang:master
Jul 25, 2017
jimblandy
referenced this pull request
Jul 26, 2017
Open
Poor error message for type definitions in `extern` blocks #43495
sdroege
referenced this pull request
Jul 29, 2017
Merged
Unions: Remove feature-gate for stable rustc #415
jplatte
referenced this pull request
Aug 31, 2017
Merged
Named existentials and impl Trait variable declarations #2071
joshtriplett
referenced this pull request
Sep 7, 2017
Merged
Support defining C-compatible variadic functions in Rust #2137
scottmcm
referenced this pull request
Jan 19, 2018
Closed
Empty structs should not need #[repr(C)] #750
Centril
added
A-typesystem
A-repr
A-dst
labels
Nov 23, 2018
sdroege
reviewed
Dec 7, 2018
| One is, we define the type as an uninhabited type. eg. | ||
|
|
||
| ```rust | ||
| enum MyFfiType {} |
This comment has been minimized.
This comment has been minimized.
sdroege
Dec 7, 2018
This is still in the RFC but wrong as it causes UB. See https://doc.rust-lang.org/nomicon/ffi.html#representing-opaque-structs and https://www.reddit.com/r/rust/comments/a3bf2p/patterns_of_refactoring_c_to_rust_the_case_of/eb5wxtw
This should probably also be fixed in the RFC text for people who look here because they want extern type but can't use it yet because it's not stable and need a workaround.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
RalfJung
Dec 7, 2018
•
Member
Ever doing *ptr is UB. This includes &*ptr as *const MyFfiType.
See https://doc.rust-lang.org/nightly/nomicon/ffi.html#representing-opaque-structs for what is currently recommended.
canndrew commentedJan 21, 2017
•
edited
Add
extern typedeclarations for declaring types from external libraries which have an unknown size/layout.Rendered