From b770adc57d06e4097b90d3492a9d04aca2703e12 Mon Sep 17 00:00:00 2001 From: Peter Atashian Date: Fri, 23 Jan 2015 07:41:03 -0500 Subject: [PATCH 1/6] Create 0000-unsafe-enums.md --- text/0000-unsafe-enums.md | 66 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 text/0000-unsafe-enums.md diff --git a/text/0000-unsafe-enums.md b/text/0000-unsafe-enums.md new file mode 100644 index 00000000000..91d4a57a093 --- /dev/null +++ b/text/0000-unsafe-enums.md @@ -0,0 +1,66 @@ +- Start Date: 2014-01-23 +- RFC PR: (leave this empty) +- Rust Issue: (leave this empty) + +# Summary + +Add an unsafe enum which is an enum without a discriminant. + +# Motivation + +When working with FFI, many C libraries will take advantage of unions. Unfortunately Rust has no way +to represent this sanely, so developers are forced to write duplicated struct definitions for each +union variant and do a lot of ugly transmuting. Unsafe enums are effectively unions, and allow FFI +that uses unions to be significantly easier. + +# Detailed design + +An unsafe enum is equivalent to a safe enum except that it does not have a discriminant. + +## Declaring an unsafe enum + +```rust +unsafe enum MyEnum { + Variant1(c_int), + Variant2(*mut c_char), + Variant3 { + x: f32, + y: f32, + }, +} +``` + +## Instantiating unsafe enums + +```rust +let foo = Variant1(5); +``` + +## Destructuring + +Due to the lack of a discriminant, `match` cannot be used to destructure an unsafe enum. +Additionally `if let` and `while let` do not make sense to support because there is no discriminant +to test. Therefore there is only one way to destructure an unsafe enum, and it is unsafe. +```rust +unsafe { let Variant1(bar) = foo; } +``` + +## Requirements on variants + +Due to the lack of a discriminant there is no way for Rust to know which variant is currently +initialized, and thus all variants of an unsafe enum are required to be `Copy` or at the very least +not `Drop`. + +# Drawbacks + +Adding unsafe enums adds more complexity to the language through a separate kind of enum which is +unusable in many of the ways that normal enums are. + +# Alternatives + +* Continue to not provide untagged unions and make life difficult for people doing FFI. +* Add a keyword such as `union`. + +# Unresolved questions + +* ??? From d100384e979e9bc54f9280c90488bd0467f40899 Mon Sep 17 00:00:00 2001 From: Peter Atashian Date: Fri, 23 Jan 2015 08:24:36 -0500 Subject: [PATCH 2/6] Update 0000-unsafe-enums.md --- text/0000-unsafe-enums.md | 43 +++++++++++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/text/0000-unsafe-enums.md b/text/0000-unsafe-enums.md index 91d4a57a093..09ecf9af183 100644 --- a/text/0000-unsafe-enums.md +++ b/text/0000-unsafe-enums.md @@ -11,7 +11,7 @@ Add an unsafe enum which is an enum without a discriminant. When working with FFI, many C libraries will take advantage of unions. Unfortunately Rust has no way to represent this sanely, so developers are forced to write duplicated struct definitions for each union variant and do a lot of ugly transmuting. Unsafe enums are effectively unions, and allow FFI -that uses unions to be significantly easier. +that uses unions to be significantly easier. The syntax chosen here replicates existing syntax without adding new keywords and is thus backwards compatible with existing code. # Detailed design @@ -38,12 +38,40 @@ let foo = Variant1(5); ## Destructuring -Due to the lack of a discriminant, `match` cannot be used to destructure an unsafe enum. -Additionally `if let` and `while let` do not make sense to support because there is no discriminant -to test. Therefore there is only one way to destructure an unsafe enum, and it is unsafe. +Due to the lack of a discriminant, destructuring becomes irrefutable, but also unsafe. Therefore all destructuring needs to be in unsafe functions or blocks. Because destructuring is irrefutable you can directly destructure unsafe enums which isn't possible with safe enums: ```rust unsafe { let Variant1(bar) = foo; } ``` +When using `match` you can only have a single irrefutable pattern. However, patterns with values inside of them or conditionals are refutable and can therefore be combined. +```rust +// Valid +unsafe match foo { + Variant2(x) => ..., +} +unsafe match foo { + Variant1(5) => ..., + Variant1(x) if x < -7 => ..., + Variant2(x) => ..., +} +// Invalid +unsafe match foo { + Variant1(x) => ..., + Variant2(x) => ..., +} +unsafe match foo { + Variant1(x) => ..., + _ => ..., +} +``` +`if let` and `while let` are irrefutable unless the pattern has a value inside. Because irrefutable `if let` and `while let` patterns are currently illegal for enums in Rust, they will continue to be illegal for unsafe enums. +```rust +// Illegal +if let Variant1(x) = foo {} +while let Variant2(y) = foo {} +// Legal +if let Variant1(5) = foo {} +while let Variant1(7) = foo {} +``` ## Requirements on variants @@ -53,14 +81,13 @@ not `Drop`. # Drawbacks -Adding unsafe enums adds more complexity to the language through a separate kind of enum which is -unusable in many of the ways that normal enums are. +Adding unsafe enums adds more complexity to the language through a separate kind of enum with its own restrictions and behavior. # Alternatives * Continue to not provide untagged unions and make life difficult for people doing FFI. -* Add a keyword such as `union`. +* Add an entirely separate type with a keyword such as `union`. # Unresolved questions -* ??? +* For `if let`, when the pattern is irrefutable should the else block be legal? From ec333f1b0b76565f3a0dda43048db913653f6619 Mon Sep 17 00:00:00 2001 From: Peter Atashian Date: Fri, 23 Jan 2015 08:27:39 -0500 Subject: [PATCH 3/6] Update 0000-unsafe-enums.md --- text/0000-unsafe-enums.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-unsafe-enums.md b/text/0000-unsafe-enums.md index 09ecf9af183..5aea7441a3d 100644 --- a/text/0000-unsafe-enums.md +++ b/text/0000-unsafe-enums.md @@ -90,4 +90,4 @@ Adding unsafe enums adds more complexity to the language through a separate kind # Unresolved questions -* For `if let`, when the pattern is irrefutable should the else block be legal? +* ??? From 4794e6d71c479ddb0d36ad6d303503ca4aefb920 Mon Sep 17 00:00:00 2001 From: Peter Atashian Date: Fri, 23 Jan 2015 08:29:15 -0500 Subject: [PATCH 4/6] Update 0000-unsafe-enums.md --- text/0000-unsafe-enums.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-unsafe-enums.md b/text/0000-unsafe-enums.md index 5aea7441a3d..b2ec133d930 100644 --- a/text/0000-unsafe-enums.md +++ b/text/0000-unsafe-enums.md @@ -51,7 +51,7 @@ unsafe match foo { unsafe match foo { Variant1(5) => ..., Variant1(x) if x < -7 => ..., - Variant2(x) => ..., + Variant1(x) => ..., } // Invalid unsafe match foo { From e352c93d2b239b686504ad842c8e018f88ae4cbf Mon Sep 17 00:00:00 2001 From: Peter Atashian Date: Fri, 23 Jan 2015 09:44:57 -0500 Subject: [PATCH 5/6] Update 0000-unsafe-enums.md --- text/0000-unsafe-enums.md | 56 ++++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 25 deletions(-) diff --git a/text/0000-unsafe-enums.md b/text/0000-unsafe-enums.md index b2ec133d930..17be401d468 100644 --- a/text/0000-unsafe-enums.md +++ b/text/0000-unsafe-enums.md @@ -31,7 +31,7 @@ unsafe enum MyEnum { ``` ## Instantiating unsafe enums - +This is completely safe. ```rust let foo = Variant1(5); ``` @@ -40,37 +40,43 @@ let foo = Variant1(5); Due to the lack of a discriminant, destructuring becomes irrefutable, but also unsafe. Therefore all destructuring needs to be in unsafe functions or blocks. Because destructuring is irrefutable you can directly destructure unsafe enums which isn't possible with safe enums: ```rust -unsafe { let Variant1(bar) = foo; } +unsafe { + let Variant1(bar) = foo; +} ``` When using `match` you can only have a single irrefutable pattern. However, patterns with values inside of them or conditionals are refutable and can therefore be combined. ```rust -// Valid -unsafe match foo { - Variant2(x) => ..., -} -unsafe match foo { - Variant1(5) => ..., - Variant1(x) if x < -7 => ..., - Variant1(x) => ..., -} -// Invalid -unsafe match foo { - Variant1(x) => ..., - Variant2(x) => ..., -} -unsafe match foo { - Variant1(x) => ..., - _ => ..., +unsafe { + // Legal + match foo { + Variant2(x) => ..., + } + match foo { + Variant1(5) => ..., + Variant1(x) if x < -7 => ..., + Variant1(x) => ..., + } + // Illegal + match foo { + Variant1(x) => ..., + Variant2(x) => ..., + } + match foo { + Variant1(x) => ..., + _ => ..., + } } ``` `if let` and `while let` are irrefutable unless the pattern has a value inside. Because irrefutable `if let` and `while let` patterns are currently illegal for enums in Rust, they will continue to be illegal for unsafe enums. ```rust -// Illegal -if let Variant1(x) = foo {} -while let Variant2(y) = foo {} -// Legal -if let Variant1(5) = foo {} -while let Variant1(7) = foo {} +unsafe { + // Legal + if let Variant1(5) = foo {...} + while let Variant1(7) = foo {...} + // Illegal + if let Variant1(x) = foo {...} + while let Variant2(y) = foo {...} +} ``` ## Requirements on variants From 54a5d9db74e5d9fdd9463e2ac2c2e920f057ed45 Mon Sep 17 00:00:00 2001 From: Peter Atashian Date: Fri, 23 Jan 2015 16:21:46 -0500 Subject: [PATCH 6/6] Update 0000-unsafe-enums.md --- text/0000-unsafe-enums.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-unsafe-enums.md b/text/0000-unsafe-enums.md index 17be401d468..353fb662586 100644 --- a/text/0000-unsafe-enums.md +++ b/text/0000-unsafe-enums.md @@ -96,4 +96,4 @@ Adding unsafe enums adds more complexity to the language through a separate kind # Unresolved questions -* ??? +* Should we require that the variants are merely `!Drop` or should we require `Copy`, or, as @eddyb suggested, should we add an opt in built in trait that represents a type where every possible bit pattern is a valid value for that type? With the latter option we would be able to make destructuring completely safe.