From 6085dc44c03506969086b3ebbe6965c8aa5b54e2 Mon Sep 17 00:00:00 2001 From: Andrew Cann Date: Sat, 21 Jan 2017 19:17:55 +0800 Subject: [PATCH 01/18] Add extern types RFC --- text/0000-extern-types.md | 110 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 text/0000-extern-types.md diff --git a/text/0000-extern-types.md b/text/0000-extern-types.md new file mode 100644 index 00000000000..00fac1514cc --- /dev/null +++ b/text/0000-extern-types.md @@ -0,0 +1,110 @@ +- Feature Name: extern_types +- Start Date: 2017-01-18 +- RFC PR: +- Rust Issue: + +# Summary +[summary]: #summary + +Add an `extern type` syntax for declaring types which are opaque to Rust's type +system. + +# Motivation +[motivation]: #motivation + +When interacting with external libraries we often need to be able to handle +pointers to data that we don't know the size or layout of. + +In C it's possible to declare a type but not define it. These incomplete types +can only be used behind pointers, a compilation error will result if the +user tries to use them in such a way that the compiler would need to know their +layout. + +In Rust, we don't have this feature. Instead, a couple of problematic hacks are +used in its place. + +One is, we define the type as an uninhabited type. eg. + + enum MyFfiType {} + +Another is, we define the type with a private field and no methods to construct +it. + + struct MyFfiType { + _priv: (), + } + +The point of both these constructions is to prevent the user from being able to +create or deal directly with instances of +the type. Niether of these types accurately reflect the reality of the +situation. The first definition is logically problematic as it defines a type +which can never exist. This means that references to the type can also - +logically - never exist and raw pointers to the type are guaranteed to be +invalid. The second definition says that the type is a ZST, that we can store +it on the stack and that we can call `ptr::read`, `mem::size_of` etc. on it. +None of this is of course valid. + +This RFC instead proposes a way to directly express that a type exists but is +unknown to Rust. + +# Detailed design +[design]: #detailed-design + +Add a new kind of type declaration, an `extern type`: + + extern type Foo; + +These can also be declared inside an `extern` block: + + extern { + type Foo; + } + +These types are FFI-safe. They are also DSTs, meaning that they implement +`?Sized`. Being DSTs, they cannot be kept on the stack and can only be +accessed through pointers. + +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. It also means +that if we store an extern type at the end of a container (such as a struct or +tuple) pointers to that container will also be identical to raw pointers +(despite the container as a whole being unsized). This is useful to support a +pattern found in some C APIs where structs are passed around which have +arbitrary data appended to the end of them: eg. + +```rust +extern type OpaqueTail; + +#[repr(C)] +struct FfiStruct { + data: u8, + more_data: u32, + tail: OpaqueTail, +} +``` + +# How We Teach This +[how-we-teach-this]: #how-we-teach-this + +This should be taught in the foreign function interface chapter of the rust +book in place of where it currently tells people to use uninhabited enums +(ack!). + +# Drawbacks +[drawbacks]: #drawbacks + +Very slight addition of complexity to the language. + +# Alternatives +[alternatives]: #alternatives + +Not do this. + +# Unresolved questions +[unresolved]: #unresolved-questions + +Should we allow generic lifetime and type parameters on extern types? If so, +how do they effect the type in terms of variance? + From b2d804220912ac4cbacdd61860572b93a6ba1ec0 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sat, 21 Jan 2017 08:42:48 -0500 Subject: [PATCH 02/18] One sentance per line so future diffs are readable. 2 and typo fixes too - niether -> neither - Use actual em dash --- text/0000-extern-types.md | 60 ++++++++++++++------------------------- 1 file changed, 22 insertions(+), 38 deletions(-) diff --git a/text/0000-extern-types.md b/text/0000-extern-types.md index 00fac1514cc..0bfd4129fc6 100644 --- a/text/0000-extern-types.md +++ b/text/0000-extern-types.md @@ -12,40 +12,32 @@ system. # Motivation [motivation]: #motivation -When interacting with external libraries we often need to be able to handle -pointers to data that we don't know the size or layout of. +When interacting with external libraries we often need to be able to handle pointers to data that we don't know the size or layout of. -In C it's possible to declare a type but not define it. These incomplete types -can only be used behind pointers, a compilation error will result if the -user tries to use them in such a way that the compiler would need to know their -layout. +In C it's possible to declare a type but not define it. +These incomplete types can only be used behind pointers, a compilation error will result if the user tries to use them in such a way that the compiler would need to know their layout. -In Rust, we don't have this feature. Instead, a couple of problematic hacks are -used in its place. +In Rust, we don't have this feature. Instead, a couple of problematic hacks are used in its place. One is, we define the type as an uninhabited type. eg. enum MyFfiType {} -Another is, we define the type with a private field and no methods to construct -it. +Another is, we define the type with a private field and no methods to construct it. struct MyFfiType { _priv: (), } -The point of both these constructions is to prevent the user from being able to -create or deal directly with instances of -the type. Niether of these types accurately reflect the reality of the -situation. The first definition is logically problematic as it defines a type -which can never exist. This means that references to the type can also - -logically - never exist and raw pointers to the type are guaranteed to be -invalid. The second definition says that the type is a ZST, that we can store -it on the stack and that we can call `ptr::read`, `mem::size_of` etc. on it. +The point of both these constructions is to prevent the user from being able to create or deal directly with instances of the type. +Neither of these types accurately reflect the reality of the situation. +The first definition is logically problematic as it defines a type which can never exist. +This means that references to the type can also—logically—never exist and raw pointers to the type are guaranteed to be +invalid. +The second definition says that the type is a ZST, that we can store it on the stack and that we can call `ptr::read`, `mem::size_of` etc. on it. None of this is of course valid. -This RFC instead proposes a way to directly express that a type exists but is -unknown to Rust. +This RFC instead proposes a way to directly express that a type exists but is unknown to Rust. # Detailed design [design]: #detailed-design @@ -60,19 +52,13 @@ These can also be declared inside an `extern` block: type Foo; } -These types are FFI-safe. They are also DSTs, meaning that they implement -`?Sized`. Being DSTs, they cannot be kept on the stack and can only be -accessed through pointers. +These types are FFI-safe. They are also DSTs, meaning that they implement `?Sized`. Being DSTs, they cannot be kept on the stack and can only be accessed through pointers. -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. It also means -that if we store an extern type at the end of a container (such as a struct or -tuple) pointers to that container will also be identical to raw pointers -(despite the container as a whole being unsized). This is useful to support a -pattern found in some C APIs where structs are passed around which have -arbitrary data appended to the end of them: eg. +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. +It also means that if we store an extern type at the end of a container (such as a struct or tuple) pointers to that container will also be identical to raw pointers (despite the container as a whole being unsized). +This is useful to support a pattern found in some C APIs where structs are passed around which have arbitrary data appended to the end of them: eg. ```rust extern type OpaqueTail; @@ -88,9 +74,8 @@ struct FfiStruct { # How We Teach This [how-we-teach-this]: #how-we-teach-this -This should be taught in the foreign function interface chapter of the rust -book in place of where it currently tells people to use uninhabited enums -(ack!). + +This should be taught in the foreign function interface chapter of the rust book in place of where it currently tells people to use uninhabited enums (ack!). # Drawbacks [drawbacks]: #drawbacks @@ -105,6 +90,5 @@ Not do this. # Unresolved questions [unresolved]: #unresolved-questions -Should we allow generic lifetime and type parameters on extern types? If so, -how do they effect the type in terms of variance? - +Should we allow generic lifetime and type parameters on extern types? +If so, how do they effect the type in terms of variance? From e6f7f93b5362941bb7d92c46055860a5c23e34b9 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sat, 21 Jan 2017 08:58:52 -0500 Subject: [PATCH 03/18] Expand teaching section --- text/0000-extern-types.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/text/0000-extern-types.md b/text/0000-extern-types.md index 0bfd4129fc6..06da07653fd 100644 --- a/text/0000-extern-types.md +++ b/text/0000-extern-types.md @@ -74,6 +74,16 @@ struct FfiStruct { # How We Teach This [how-we-teach-this]: #how-we-teach-this +Really, the question is "how do we teach *without* this". +As described above, the current tricks for doing this are wrong. +Furthermore, they are quite advanced touching upon many advanced corners of the language: zero-sized and uninhabited types are phenomena few programmer coming from mainstream languages have considered. +From reading around other RFCs, issues, and internal threads, one gets a sense of two issues: +First, even among the group Rust programmers enthusiastic enough to participate in these fora, the semantics of foreign types are not widely understood. +Send, there is annoyance that none of the current tricks, by nature of them all being flawed in different ways, would become standard. + +By contrast, `extern type` does exactly what one wants, with an obvious and guessable syntax, without forcing the user to immediately understand all the nuance about why *these* semantics are indeed the right ones. +As they see various options fail: moves, stack variables, they can discover these semantics incrementally. +The benefits are such that this would soon displace the current hacks, making code in the wild more readable through consistent use of a pattern. This should be taught in the foreign function interface chapter of the rust book in place of where it currently tells people to use uninhabited enums (ack!). From 083adaddbf268bf1dcb1c6b59c9fd89a8d329a6e Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sat, 21 Jan 2017 09:02:46 -0500 Subject: [PATCH 04/18] Shout-out to roadmap with it's focus on FFI in the motivation section --- text/0000-extern-types.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/text/0000-extern-types.md b/text/0000-extern-types.md index 06da07653fd..96975a88cbb 100644 --- a/text/0000-extern-types.md +++ b/text/0000-extern-types.md @@ -39,6 +39,9 @@ None of this is of course valid. This RFC instead proposes a way to directly express that a type exists but is unknown to Rust. +Finally, In the 2017 roadmap, [integration with other languages](https://github.com/rust-lang/rfcs/blob/master/text/1774-roadmap-2017.md#integration-with-other-languages), is listed as a priority. +Just like unions, this is an unsafe feature necessary for dealing with legacy code in a correct and understandable manner. + # Detailed design [design]: #detailed-design From 2f7731663801a781298a2fd4adea18a54df25a43 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sat, 21 Jan 2017 09:04:09 -0500 Subject: [PATCH 05/18] Add ```rust in a few more places to get syntax highlighting --- text/0000-extern-types.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/text/0000-extern-types.md b/text/0000-extern-types.md index 96975a88cbb..709684aca3f 100644 --- a/text/0000-extern-types.md +++ b/text/0000-extern-types.md @@ -21,13 +21,17 @@ In Rust, we don't have this feature. Instead, a couple of problematic hacks are One is, we define the type as an uninhabited type. eg. +```rust enum MyFfiType {} +``` Another is, we define the type with a private field and no methods to construct it. +```rust struct MyFfiType { _priv: (), } +``` The point of both these constructions is to prevent the user from being able to create or deal directly with instances of the type. Neither of these types accurately reflect the reality of the situation. @@ -47,13 +51,17 @@ Just like unions, this is an unsafe feature necessary for dealing with legacy co Add a new kind of type declaration, an `extern type`: +```rust extern type Foo; +``` These can also be declared inside an `extern` block: +```rust extern { type Foo; } +``` These types are FFI-safe. They are also DSTs, meaning that they implement `?Sized`. Being DSTs, they cannot be kept on the stack and can only be accessed through pointers. From 949c292ec524afb83803c8ba3c07ad91d70003a6 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sat, 21 Jan 2017 09:12:47 -0500 Subject: [PATCH 06/18] Mention `size_of_val`/`align_of_val` in detailed design --- text/0000-extern-types.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/text/0000-extern-types.md b/text/0000-extern-types.md index 709684aca3f..3a2ca300a87 100644 --- a/text/0000-extern-types.md +++ b/text/0000-extern-types.md @@ -82,6 +82,10 @@ struct FfiStruct { } ``` +As a DST, `size_of` and `align_of` do not work, but we must also be careful that `size_of_val` and `align_of_val` do not work either, as there is not necessarily a way at run-time to get the size of extern types either. +For an initial implementation, those methods can just panic, but before this is stabilized there should be some trait bound or similar on them that prevents their use statically. +The exact mechanism is more the domain of the custom DST RFC, [RFC 1524](https://github.com/rust-lang/rfcs/pull/1524), and so figuring that mechanism out will be delegated to it. + # How We Teach This [how-we-teach-this]: #how-we-teach-this From 9f6d0989393ed9abbd84eaa7b4cc5de160b08680 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sat, 21 Jan 2017 09:20:08 -0500 Subject: [PATCH 07/18] OOps --- text/0000-extern-types.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/text/0000-extern-types.md b/text/0000-extern-types.md index 3a2ca300a87..a799d141f06 100644 --- a/text/0000-extern-types.md +++ b/text/0000-extern-types.md @@ -66,7 +66,8 @@ These can also be declared inside an `extern` block: These types are FFI-safe. They are also DSTs, meaning that they implement `?Sized`. Being DSTs, they cannot be kept on the stack and can only be accessed through pointers. 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 `()`. +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. It also means that if we store an extern type at the end of a container (such as a struct or tuple) pointers to that container will also be identical to raw pointers (despite the container as a whole being unsized). This is useful to support a pattern found in some C APIs where structs are passed around which have arbitrary data appended to the end of them: eg. From cf183e93cacb2d9649d4476504370a7905c26c29 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sat, 21 Jan 2017 09:21:40 -0500 Subject: [PATCH 08/18] "Sized" is the trait, not "?Sized" --- text/0000-extern-types.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-extern-types.md b/text/0000-extern-types.md index a799d141f06..20bbd122364 100644 --- a/text/0000-extern-types.md +++ b/text/0000-extern-types.md @@ -63,7 +63,7 @@ These can also be declared inside an `extern` block: } ``` -These types are FFI-safe. They are also DSTs, meaning that they implement `?Sized`. Being DSTs, they cannot be kept on the stack and can only be accessed through pointers. +These types are FFI-safe. They are also DSTs, meaning that they do not implement `Sized`. Being DSTs, they cannot be kept on the stack and can only be accessed through pointers. 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. From 434b472df883266d2e1f4b9b00f2530e6f125310 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sat, 21 Jan 2017 09:43:06 -0500 Subject: [PATCH 09/18] Talk about `c_void`, and associated representation concerns --- text/0000-extern-types.md | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/text/0000-extern-types.md b/text/0000-extern-types.md index 20bbd122364..a7aa2b8746f 100644 --- a/text/0000-extern-types.md +++ b/text/0000-extern-types.md @@ -41,6 +41,8 @@ invalid. The second definition says that the type is a ZST, that we can store it on the stack and that we can call `ptr::read`, `mem::size_of` etc. on it. None of this is of course valid. +The controversies on how to represent foreign types even extend to the standard library too; see the discussion in the [libc_types RFC PR](https://github.com/rust-lang/rfcs/pull/1783). + This RFC instead proposes a way to directly express that a type exists but is unknown to Rust. Finally, In the 2017 roadmap, [integration with other languages](https://github.com/rust-lang/rfcs/blob/master/text/1774-roadmap-2017.md#integration-with-other-languages), is listed as a priority. @@ -87,6 +89,10 @@ As a DST, `size_of` and `align_of` do not work, but we must also be careful that For an initial implementation, those methods can just panic, but before this is stabilized there should be some trait bound or similar on them that prevents their use statically. The exact mechanism is more the domain of the custom DST RFC, [RFC 1524](https://github.com/rust-lang/rfcs/pull/1524), and so figuring that mechanism out will be delegated to it. +C's "pointer `void`" (not `()`, but the `void` used in `void*` and similar) is currently defined in two official places: [`std::os::raw::c_void`](https://doc.rust-lang.org/stable/std/os/raw/enum.c_void.html) and [`libc::c_void`](https://doc.rust-lang.org/libc/x86_64-unknown-linux-gnu/libc/enum.c_void.html). +Unifying these is out of scope for this RFC, but this feature should be used in their definition instead of the current tricks. +Strictly speaking, this is breaking change, but the `std` docs explicitly say this shouldn't be used without indirection, and `libc` worse-case can make a breaking release. + # How We Teach This [how-we-teach-this]: #how-we-teach-this @@ -116,5 +122,10 @@ Not do this. # Unresolved questions [unresolved]: #unresolved-questions -Should we allow generic lifetime and type parameters on extern types? -If so, how do they effect the type in terms of variance? +- Should we allow generic lifetime and type parameters on extern types? + If so, how do they effect the type in terms of variance? + +- [In std's source](https://github.com/rust-lang/rust/blob/164619a8cfe6d376d25bd3a6a9a5f2856c8de64d/src/libstd/os/raw.rs#L59-L64), it is mentioned that LLVM expects `i8*` for C's `void*`. + We'd need to continue to hack this for the two `c_void`s in std and libc. + But perhaps this should be done across-the-board for all extern types? + Somebody should check what Clang does. From bce8a7bcb900ce86816ab2b7ddb309a7e5f8d5db Mon Sep 17 00:00:00 2001 From: Andrew Cann Date: Sun, 22 Jan 2017 01:51:59 +0800 Subject: [PATCH 10/18] Grammatic changes --- text/0000-extern-types.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/text/0000-extern-types.md b/text/0000-extern-types.md index a7aa2b8746f..442e2b5a933 100644 --- a/text/0000-extern-types.md +++ b/text/0000-extern-types.md @@ -91,7 +91,8 @@ The exact mechanism is more the domain of the custom DST RFC, [RFC 1524](https:/ C's "pointer `void`" (not `()`, but the `void` used in `void*` and similar) is currently defined in two official places: [`std::os::raw::c_void`](https://doc.rust-lang.org/stable/std/os/raw/enum.c_void.html) and [`libc::c_void`](https://doc.rust-lang.org/libc/x86_64-unknown-linux-gnu/libc/enum.c_void.html). Unifying these is out of scope for this RFC, but this feature should be used in their definition instead of the current tricks. -Strictly speaking, this is breaking change, but the `std` docs explicitly say this shouldn't be used without indirection, and `libc` worse-case can make a breaking release. +Strictly speaking, this is a breaking change, but the `std` docs explicitly say that `void` shouldn't be used without indirection. +And `libc` can, in the worst-case, make a breaking change. # How We Teach This [how-we-teach-this]: #how-we-teach-this From 156ed4b6ade0bc93e51696d1ee44bf37fac65450 Mon Sep 17 00:00:00 2001 From: Andrew Cann Date: Sun, 22 Jan 2017 01:57:41 +0800 Subject: [PATCH 11/18] Remove indentation and invalid syntax --- text/0000-extern-types.md | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/text/0000-extern-types.md b/text/0000-extern-types.md index 442e2b5a933..3962ebcda59 100644 --- a/text/0000-extern-types.md +++ b/text/0000-extern-types.md @@ -22,15 +22,15 @@ In Rust, we don't have this feature. Instead, a couple of problematic hacks are One is, we define the type as an uninhabited type. eg. ```rust - enum MyFfiType {} +enum MyFfiType {} ``` Another is, we define the type with a private field and no methods to construct it. ```rust - struct MyFfiType { - _priv: (), - } +struct MyFfiType { + _priv: (), +} ``` The point of both these constructions is to prevent the user from being able to create or deal directly with instances of the type. @@ -51,18 +51,12 @@ Just like unions, this is an unsafe feature necessary for dealing with legacy co # Detailed design [design]: #detailed-design -Add a new kind of type declaration, an `extern type`: +Add a new kind of type declaration, an extern type: ```rust - extern type Foo; -``` - -These can also be declared inside an `extern` block: - -```rust - extern { - type Foo; - } +extern { + type Foo; +} ``` These types are FFI-safe. They are also DSTs, meaning that they do not implement `Sized`. Being DSTs, they cannot be kept on the stack and can only be accessed through pointers. @@ -75,7 +69,9 @@ It also means that if we store an extern type at the end of a container (such as This is useful to support a pattern found in some C APIs where structs are passed around which have arbitrary data appended to the end of them: eg. ```rust -extern type OpaqueTail; +extern { + type OpaqueTail; +} #[repr(C)] struct FfiStruct { From caa048bfc7f5a1a1ef3fecf08808fe85f6bae6ff Mon Sep 17 00:00:00 2001 From: Andrew Cann Date: Mon, 23 Jan 2017 19:24:55 +0800 Subject: [PATCH 12/18] Fix typo --- text/0000-extern-types.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-extern-types.md b/text/0000-extern-types.md index 3962ebcda59..2b961d4d45a 100644 --- a/text/0000-extern-types.md +++ b/text/0000-extern-types.md @@ -98,7 +98,7 @@ As described above, the current tricks for doing this are wrong. Furthermore, they are quite advanced touching upon many advanced corners of the language: zero-sized and uninhabited types are phenomena few programmer coming from mainstream languages have considered. From reading around other RFCs, issues, and internal threads, one gets a sense of two issues: First, even among the group Rust programmers enthusiastic enough to participate in these fora, the semantics of foreign types are not widely understood. -Send, there is annoyance that none of the current tricks, by nature of them all being flawed in different ways, would become standard. +Second, there is annoyance that none of the current tricks, by nature of them all being flawed in different ways, would become standard. By contrast, `extern type` does exactly what one wants, with an obvious and guessable syntax, without forcing the user to immediately understand all the nuance about why *these* semantics are indeed the right ones. As they see various options fail: moves, stack variables, they can discover these semantics incrementally. From d76b60807de611a12c5738d2036a6d96f429a968 Mon Sep 17 00:00:00 2001 From: Andrew Cann Date: Fri, 27 Jan 2017 20:21:59 +0800 Subject: [PATCH 13/18] Mention OpaqueData as an alternative --- text/0000-extern-types.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/text/0000-extern-types.md b/text/0000-extern-types.md index 2b961d4d45a..00840f6611c 100644 --- a/text/0000-extern-types.md +++ b/text/0000-extern-types.md @@ -116,6 +116,10 @@ Very slight addition of complexity to the language. Not do this. +Alternatively, rather than provide a way to create opaque types, we could just offer one distinguished type (`std::mem::OpaqueData` or something like that). +Then, to create new opaque types, users just declare a struct with a member of type `OpaqueData`. +This has the advantage of introducing no new syntax, and issues like FFI-compatibility would fall out of existing rules. + # Unresolved questions [unresolved]: #unresolved-questions From a5907676e177fd0fa53b42e945ff7157cd000e78 Mon Sep 17 00:00:00 2001 From: Andrew Cann Date: Sun, 30 Apr 2017 16:32:40 +0800 Subject: [PATCH 14/18] grammar/typos --- text/0000-extern-types.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/text/0000-extern-types.md b/text/0000-extern-types.md index 00840f6611c..150e7162057 100644 --- a/text/0000-extern-types.md +++ b/text/0000-extern-types.md @@ -95,9 +95,9 @@ And `libc` can, in the worst-case, make a breaking change. Really, the question is "how do we teach *without* this". As described above, the current tricks for doing this are wrong. -Furthermore, they are quite advanced touching upon many advanced corners of the language: zero-sized and uninhabited types are phenomena few programmer coming from mainstream languages have considered. +Furthermore, they are quite advanced touching upon many advanced corners of the language: zero-sized and uninhabited types are phenomena few programmer coming from mainstream languages have encountered. From reading around other RFCs, issues, and internal threads, one gets a sense of two issues: -First, even among the group Rust programmers enthusiastic enough to participate in these fora, the semantics of foreign types are not widely understood. +First, even among the group of Rust programmers enthusiastic enough to participate in these fora, the semantics of foreign types are not widely understood. Second, there is annoyance that none of the current tricks, by nature of them all being flawed in different ways, would become standard. By contrast, `extern type` does exactly what one wants, with an obvious and guessable syntax, without forcing the user to immediately understand all the nuance about why *these* semantics are indeed the right ones. From 9efddd3da6fc4b43bf448dcc347619456507dae6 Mon Sep 17 00:00:00 2001 From: Andrew Cann Date: Sun, 30 Apr 2017 16:48:54 +0800 Subject: [PATCH 15/18] Update with feedback --- text/0000-extern-types.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/text/0000-extern-types.md b/text/0000-extern-types.md index 150e7162057..0923ca08c6e 100644 --- a/text/0000-extern-types.md +++ b/text/0000-extern-types.md @@ -59,7 +59,7 @@ extern { } ``` -These types are FFI-safe. They are also DSTs, meaning that they do not implement `Sized`. Being DSTs, they cannot be kept on the stack and can only be accessed through pointers. +These types are FFI-safe. They are also DSTs, meaning that they do not implement `Sized`. Being DSTs, they cannot be kept on the stack, can only be accessed through pointers and references and cannot be moved from. 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. @@ -111,6 +111,9 @@ This should be taught in the foreign function interface chapter of the rust book Very slight addition of complexity to the language. +The syntax has the potential to be confused with introducing a type alias, rather than a new nominal type. +The use of `extern` here is also a bit of a misnomer as the name of the type does not refer to anything external to Rust. + # Alternatives [alternatives]: #alternatives @@ -120,6 +123,9 @@ Alternatively, rather than provide a way to create opaque types, we could just o Then, to create new opaque types, users just declare a struct with a member of type `OpaqueData`. This has the advantage of introducing no new syntax, and issues like FFI-compatibility would fall out of existing rules. +Another alternative is to drop the `extern` and allow a declaration to be written `type A;`. +This removes the (arguably disingenuous) use of the `extern` keyword although it makes the syntax look even more like a type alias. + # Unresolved questions [unresolved]: #unresolved-questions From 263b7c9cbf2038502af899619f9e7ce70b079df7 Mon Sep 17 00:00:00 2001 From: Andrew Cann Date: Mon, 24 Jul 2017 17:59:20 +0800 Subject: [PATCH 16/18] Clarify that extern type pointers are thin pointers --- text/0000-extern-types.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-extern-types.md b/text/0000-extern-types.md index 0923ca08c6e..d4218218310 100644 --- a/text/0000-extern-types.md +++ b/text/0000-extern-types.md @@ -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"). It also means that if we store an extern type at the end of a container (such as a struct or tuple) pointers to that container will also be identical to raw pointers (despite the container as a whole being unsized). This is useful to support a pattern found in some C APIs where structs are passed around which have arbitrary data appended to the end of them: eg. From e884a0b98353bc6e6f7b775e94129186688f1d31 Mon Sep 17 00:00:00 2001 From: Andrew Cann Date: Tue, 25 Jul 2017 11:44:58 +0800 Subject: [PATCH 17/18] re-word last commit --- text/0000-extern-types.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-extern-types.md b/text/0000-extern-types.md index d4218218310..632bfc50c7e 100644 --- a/text/0000-extern-types.md +++ b/text/0000-extern-types.md @@ -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 (ie. it is not a "fat pointer"). +This means that a pointer to an extern type has the same size as a `usize` (ie. it is not a "fat pointer"). It also means that if we store an extern type at the end of a container (such as a struct or tuple) pointers to that container will also be identical to raw pointers (despite the container as a whole being unsized). This is useful to support a pattern found in some C APIs where structs are passed around which have arbitrary data appended to the end of them: eg. From 4b971bdfebd95a667ca257965684f361d7bee9be Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Mon, 24 Jul 2017 21:13:07 -0700 Subject: [PATCH 18/18] RFC 1861 is Extern types --- text/{0000-extern-types.md => 1861-extern-types.md} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename text/{0000-extern-types.md => 1861-extern-types.md} (98%) diff --git a/text/0000-extern-types.md b/text/1861-extern-types.md similarity index 98% rename from text/0000-extern-types.md rename to text/1861-extern-types.md index 632bfc50c7e..baeb5c3d609 100644 --- a/text/0000-extern-types.md +++ b/text/1861-extern-types.md @@ -1,7 +1,7 @@ - Feature Name: extern_types - Start Date: 2017-01-18 -- RFC PR: -- Rust Issue: +- RFC PR: https://github.com/rust-lang/rfcs/pull/1861 +- Rust Issue: https://github.com/rust-lang/rust/issues/43467 # Summary [summary]: #summary