@@ -757,6 +757,149 @@ Unsafe Fields Denote Safety Invariants**](#tenet-unsafe-fields-denote-safety-inv
757
757
requiring trivially ` unsafe ` drop glue), a violation of [ ** Tenet: Safe Usage is Usually
758
758
Safe** ] ( #tenet-safe-usage-is-usually-safe ) .
759
759
760
+ ### Unsafe Wrapper Type
761
+
762
+ This RFC proposes extending the Rust language with first-class support for field (un)safety.
763
+ Alternatively, we could attempt to achieve the same effects by leveraging Rust's existing visibility
764
+ and safety affordances. At first blush, this seems plausible; it's trivial to define a wrapper that
765
+ only provides unsafe initialization and access to its value:
766
+
767
+ ``` rust
768
+ #[repr(transparent)]
769
+ pub struct Unsafe <T : ? Sized >(T );
770
+
771
+ impl <T : ? Sized > Unsafe <T > {
772
+
773
+ pub unsafe fn new (val : T ) -> Self
774
+ where
775
+ T : Sized
776
+ {
777
+ Self (val )
778
+ }
779
+
780
+ pub unsafe fn as_ref (& self ) -> & T {
781
+ & self . 0
782
+ }
783
+
784
+ pub unsafe fn as_mut (& mut self ) -> & mut T {
785
+ & mut self . 0
786
+ }
787
+
788
+ pub unsafe fn into_inner (self ) -> T
789
+ where
790
+ T : Sized
791
+ {
792
+ self . 0
793
+ }
794
+ }
795
+ ```
796
+
797
+ However, this falls short of the assurances provided by first-class support for field safety.
798
+ ` Unsafe::new ` inherits the safety conditions of the field that the ` Unsafe ` will be assigned to, and
799
+ the safety conditions of its accessors inherit the safety conditions of the field that the ` Unsafe `
800
+ was read or referenced from. Consequently, what safety proofs one must write when using such a
801
+ wrapper are a product of the dataflow of the program.
802
+
803
+ And worse, certain dangerous flows do not require ` unsafe ` at all. For instance, unsafe fields of
804
+ the same type can be laundered between fields with different invariants; safe code could exchange
805
+ ` Even ` and ` Odd ` s' ` val ` s:
806
+
807
+ ``` rust
808
+ struct Even {
809
+ val : Unsafe <usize >,
810
+ }
811
+
812
+ struct Odd {
813
+ val : Unsafe <usize >,
814
+ }
815
+ ```
816
+
817
+ We can plug this particular hole by adding a type parameter to ` Unsafe ` that encodes the type of the
818
+ outer datatype, ` O ` ; e.g.:
819
+
820
+ ``` rust
821
+ #[repr(transparent)]
822
+ pub struct Unsafe <O : ? Sized , T : ? Sized >(PhantomData <O >, T );
823
+ ```
824
+
825
+ However, it remains possible to exchange unsafe fields within the same type; for example, safe code
826
+ can freely exchange the values of ` len ` and ` cap ` of this hypothetical vector:
827
+
828
+ ``` rust
829
+ struct Vec <T > {
830
+ alloc : Unsafe <Self , * mut T >,
831
+ len : Unsafe <Self , usize >,
832
+ cap : Unsafe <Self , usize >,
833
+ }
834
+ ```
835
+
836
+ The [ ` unsafe-fields ` ] ( https://crates.io/crates/unsafe-fields ) crate plugs this hole by extending
837
+ ` Unsafe ` with a const generic that holds a hash of the field name. Even so, it remains possible for
838
+ safe code to exchange the same unsafe field between different instances of the same type (e.g.,
839
+ exchanging the ` len ` s of two instances of the aforementioned ` Vec ` ).
840
+
841
+ These challenges motivate first-class support for field safety tooling.
842
+
843
+ ## More Syntactic Granularity
844
+
845
+ This RFC proposes the rule that * a field marked ` unsafe ` is unsafe to use* . This rule is flexible
846
+ enough to handle arbitrary field invariants, but — in some scenarios — requires that the user write
847
+ trivial safety comments. For example, in some scenarios, an unsafe is trivially sound to read:
848
+
849
+ ``` rust
850
+ struct Even {
851
+ /// # Safety
852
+ ///
853
+ /// `val` is an even number.
854
+ val : u8 ,
855
+ }
856
+
857
+ impl Into <u8 > for Even {
858
+ fn into (self ) -> u8 {
859
+ // SAFETY: Reading this `val` cannot
860
+ // violate its invariant.
861
+ unsafe { self . val }
862
+ }
863
+ }
864
+ ```
865
+
866
+ In other scenarios, an unsafe field is trivially sound to ` & ` -reference (but not ` &mut ` -reference).
867
+
868
+ Since it is impossible for the compiler to precisely determine the safety requirements of an unsafe
869
+ field from a type-directed analysis, we must * either* choose a usage rule that fits all scenarios
870
+ (i.e., the approach adopted by this RFC) * or* provide the user with a mechanism to signal their
871
+ requirements to the compiler. Here, we explore this alternative.
872
+
873
+ The design space of syntactic knobs is vast. For instance, we could require that the user enumerate
874
+ the operations that require ` unsafe ` ; e.g.:
875
+
876
+ - ` unsafe(init,&mut,&,read) ` (everything is unsafe)
877
+ - ` unsafe(init,&mut,&) ` (everything except reading unsafe)
878
+ - ` unsafe(init,&mut) ` (everything except reading and ` & ` -referencing unsafe)
879
+ - etc.
880
+
881
+ Besides the unclear semantics of an unparameterized ` unsafe() ` , this design has the disadvantage
882
+ that the most permissive (and thus dangerous) semantics are the cheapest to type. To mitigate this,
883
+ we might instead imagine reversing the polarity of the modifier:
884
+
885
+ - ` safe(read) ` all operations except reading are safe
886
+ - ` safe(read,&) ` all operations except reading and ` & ` -referencing are safe
887
+ - etc.
888
+
889
+ ...but using ` safe ` to denote the presence of a safety invariant is probably too surprising in the
890
+ context of Rust's existing safety tooling.
891
+
892
+ Alternatively, if we are confident that a hierarchy of operations exists, the brevity of the API can
893
+ be improved by having the presence of one modifier imply others (e.g., ` unsafe(&mut) ` could denote
894
+ that initialization, mutation and ` &mut ` -referencing) are unsafe. However, this requires that the
895
+ user internalize this hierarchy, or else risk selecting the wrong modifier for their invariant.
896
+
897
+ Although we cannot explore the entire design space of syntactic modifiers here, we broadly feel that
898
+ their additional complexity exceeds that of our proposed design. Our proposed rule that * a field
899
+ marked ` unsafe ` is unsafe to use* is both pedagogically simple and fail safe; i.e., so long as a
900
+ field is marked ` unsafe ` , it cannot be misused in such a way that its invariant is violated in safe
901
+ code.
902
+
760
903
# Drawbacks
761
904
762
905
## Alarm Fatigue
0 commit comments