John S Wrenn
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
      • Invitee
    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Versions and GitHub Sync Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
Invitee
Publish Note

Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

Your note will be visible on your profile and discoverable by anyone.
Your note is now live.
This note is visible on your profile and discoverable online.
Everyone on the web can find and read all notes of this public team.
See published notes
Unpublish note
Please check the box to agree to the Community Guidelines.
View profile
Engagement control
Commenting
Permission
Disabled Forbidden Owners Signed-in users Everyone
Enable
Permission
  • Forbidden
  • Owners
  • Signed-in users
  • Everyone
Suggest edit
Permission
Disabled Forbidden Owners Signed-in users Everyone
Enable
Permission
  • Forbidden
  • Owners
  • Signed-in users
Emoji Reply
Enable
Import from Dropbox Google Drive Gist Clipboard
   owned this note    owned this note      
Published Linked with GitHub
Subscribed
  • Any changes
    Be notified of any changes
  • Mention me
    Be notified of mention me
  • Unsubscribe
Subscribe
# Unsafe Fields Design Document #### Helpful Links - [RFC 3458](https://github.com/rust-lang/rfcs/pull/3458) - [Zulip Topic](https://rust-lang.zulipchat.com/#narrow/channel/213817-t-lang/topic/unsafe.20fields.20RFC) - [Experimental Implementation](https://github.com/veluca93/rust/tree/unsafe-fields) ## Overview This RFC proposes extending Rust's tooling support for safety hygiene to named fields that carry library safety invariants. Consequently, Rust programmers will be able to use the `unsafe` keyword to denote when a named field carries a library safety invariant; e.g.: ```rust struct UnalignedRef<'a, T> { /// # Safety /// /// `ptr` is a shared reference to a valid-but-unaligned instance of `T`. unsafe ptr: *const T, _lifetime: PhantomData<&'a T>, } ``` Rust will enforce that potentially-invalidating uses of `unsafe` fields only occur in the context of an `unsafe` block, and Clippy's [`missing_safety_doc`] lint will check that `unsafe` fields have accompanying safety documentation. [`missing_safety_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_safety_doc ## Status Quo and Motivations Safety hygiene is the practice of denoting and documenting where memory safety obligations arise and where they are discharged. Rust provides some tooling support for this practice. For example, if a function has safety obligations that must be discharged by its callers, that function *should* be marked `unsafe` and documentation about its invariants *should* be provided (this is optionally enforced by Clippy via the [missing_safety_doc](https://rust-lang.github.io/rust-clippy/master/index.html#missing_safety_doc) lint). Consumers, then, *must* use the `unsafe` keyword to call it (this is enforced by rustc), and *should* explain why its safety obligations are discharged (again, optionally enforced by Clippy). Functions are often marked `unsafe` because they concern the safety invariants of fields. For example, [`Vec::set_len`](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.set_len) is `unsafe`, because it directly manipulates its `Vec`'s length field, which carries the invariants that it is less than the capacity of the `Vec` and that all elements in the `Vec<T>` between 0 and `len` are valid `T`. It is critical that these invariants are upheld; if they are violated invoking many of `Vec`'s other, safe methods induces undefined behavior. To help ensure these invariants are upheld, programmers may apply safety hygiene techniques to fields, denoting when they carry invariants and documenting why their uses satisfy their invariants. For example, the `zerocopy` crate maintains the policy that fields with safety invariants have `# Safety` documentation, and that uses of those fields occur in the lexical context of an `unsafe` block with a suitable `// SAFETY` comment. Unfortunately, Rust does not yet provide tooling for this practice declaring, discharging, or documenting the safety invariants of fields. Since the `unsafe` keyword cannot be applied to field definitions, Rust cannot enforce that potentially-invalidating uses of fields occur in the context of `unsafe` blocks, and thus Clippy cannot enforce that safety comments are present either at definition or use sites. This RFC is motivated by the benefits of closing this tooling gap. ### Motivation: Improving Field Safety Hygiene The absence of the safety tooling support for fields makes practice of good field safety hygiene entirely a matter of programmer discipline, and, consequently, the practice of good field safety hygiene is nascent. Rust's visibility mechanisms can, to some extent, be (ab)used to help enforce good field safety hygiene. For example, zerocopy's [`Ptr` type is defined in a private `def` module](https://docs.rs/zerocopy/0.8.14/src/zerocopy/pointer/ptr.rs.html#13-121), which solely contains the datatype definition and an impl containing `pub(super)` `unsafe` constructors, getters and setters. All other `impl`s of `Ptr` are defined outside of this module and therefore must mediate their access to `Ptr`'s private fields through these unsafe functions. This roundabout approach poses significant linguistic friction and may be untenable when split borrows are required. Consequently, this approach is uncommon in the Rust ecosystem. We hope that less friction and better tooling will make good field safety hygiene more common in the Rust ecosystem. ### Motivation: Improving Function Safety Hygiene Rust's safety tooling ensures that `unsafe` operations may only occur in the lexical context of an `unsafe` block or function. If the safety obligations of an operation cannot be discharged entirely by an `unsafe` block, then the surrounding function must, itself, be `unsafe`. This tooling cue nudges programmers towards good function hygiene. But, presently, it has a shortcoming: dangerous field uses are not linted against. The unsafe `Vec::set_len` method, for example, contains entirely safe code. There is no tooling cue that suggests this function should be unsafe — only programmer knowledge. Extending safety tooling to fields will close this gap. ### Motivation: Making Rust Easier to Audit To evaluate the soundness of `unsafe` code (i.e., code which relies on safety invariants being upheld), it is not enough for reviewers to check the contents of `unsafe` blocks — they must check *all* places (including safe contexts) in which safety invariants might be violated. (See [*The Scope of Unsafe*].) This is, in large part, because safety tooling does not extend to fields. Consequently, safety invariants may be violated at-a-distance in safe code, and safety audits must therefore carefully consider distant safe code. Crates that practice good field safety hygiene will be easier to review. While reviewers must still ensure that fields which carry safety invariants are actually marked `unsafe`, having done so, they may largely limit their review to `unsafe` code and (in the absence of unsafe local bindings) safe code in the same function. [*The Scope of Unsafe*]: https://www.ralfj.de/blog/2016/01/09/the-scope-of-unsafe.html ### Other Benefits - Unsafe fields provide the libs team a knob with which to revisit the (un)safety of traits without breaking users, since trait can be made conditionally safe to implement, depending on whether the target type has unsafe fields. This RFC proposes that `Copy` gets this treatment; `Unpin` and `UnwindSafe` may also be a compelling candidates. ## Design Tenets The design of `unsafe` fields is guided by three tenets: 1. [**Unsafe Fields Denote Safety Invariants**](#1-Unsafe-Fields-Denote-Safety-Invariants) A field *should* be marked `unsafe` if it carries arbitrary library safety invariants with respect to its enclosing type. 2. [**Unsafe Usage is Always Unsafe**](#2-Unsafe-Usage-is-Always-Unsafe) Uses of `unsafe` fields which could violate their invariants *must* occur in the scope of an `unsafe` block. 3. [**Safe Usage is Usually Safe**](#3-Safe-Usage-is-Usually-Safe) Uses of `unsafe` fields which cannot violate their invariants *should not* require an unsafe block. ## Glossary - **safety invariant** A *safety invariant* is a boolean statement about the state of the computer at time *t*. - **language safety invariant** A *language safety invariant* is a safety invariant guaranteed by the language such that the compiler may reason about it. Language safety invariants must always hold. For example, a `NonZeroU8` must **never** be `0`. - **library safety invariant** A *library safety invariant* is a safety invariant assumed to be true by an API. For example, `str` encapsulates valid UTF-8 bytes, and much of its API assumes this to be true. However, this invariant may be temporarily violated, so long as no code that assumes this safety invariant holds is invoked. - **implicit constructor** The implicit constructor of a Rust type is the one that's provided by Rust upon defining a type. For example, defining `struct Foo(u8)` implicitly introduces a function named `Foo` which consumes a `u8` and produces a `Foo`. ## 1. Unsafe Fields Denote Safety Invariants > A field *should* be marked `unsafe` if it carries library safety invariants with respect to its enclosing type. ### Rationale This purpose is consistent with the purpose of the `unsafe` keyword in other declaration positions, where it signals to consumers of the `unsafe` item that their consumption is conditional on upholding safety invariants; for example: - An `unsafe` trait denotes that it carries safety invariants which must be upheld by implementors. - An `unsafe` function denotes that it carries safety invariants which must be upheld by callers. #### SHOULD vs MUST A field carrying safety invariants *should* — not *must* — be marked `unsafe`. We cannot programatically enforce that fields which carry safety invariants are marked `unsafe`, just as we cannot enforce that functions with safety invariants are marked unsafe. The use of `unsafe` in declaration position is a social contract. We also cannot immediately change Rust's social contract, since doing so would mean that code which is currently compliant with Rust's social contract (which does not and cannot require that `unsafe` fields are marked with `unsafe`) would cease to be compliant. At best, we may be able to evolve Rust's social contract over an edition boundary. ### Example: Field with Local Invariant In the simplest case, a field's safety invariant is a restriction of the invariants imposed by the field type, and concern only the immediate value of the field; e.g.: ```rust struct Alignment { /// SAFETY: `pow` must be between 0 and 29. pub unsafe pow: u8, } ``` ### Example: Field with Referent Invariant A field might carry an invariant with respect to its referent; e.g.: ```rust struct CacheArcCount<T> { /// SAFETY: This `Arc`'s `ref_count` must equal the /// value of the `ref_count` field. unsafe arc: Arc<T>, /// SAFETY: See [`CacheArcCount::arc`]. unsafe ref_count: usize, } ``` ### Example: Field with External Invariant A field might carry an invariant with respect to data outside of the Rust abstract machine. ```rust struct Zeroator { /// SAFETY: The fd points to a uniquely-owned file, /// and the bytes from the start of the file to the /// offset `cursor` (exclusive) are zero. unsafe fd: OwnedFd, /// SAFETY: See [`Zeroator::fd`]. unsafe cursor: usize, } ``` ### Example: Field with Suspended Invariant A field safety invariant might also be a *relaxation* of the safety invariants imposed by the field type. For example, a `str` is bound by both the language safety invariant that it is initialized bytes, and by the library safety invariant invariant that it contains valid UTF-8. It is sound to temporarily violate the library invariant of `str`, so long as the invalid `str` is not exposed to code that might assume `str` validity. Below, `MaybeInvalidStr` encapsulates an initialized-but-potentially-invalid `str` as an unsafe field: ```rust struct MyabeInvalidStr<'a> { /// SAFETY: `maybe_invalid` may not contain valid /// UTF-8. It MUST always contain initialized /// bytes (per language safety invariant on `str`). pub unsafe maybe_invalid: &'a str } ``` ### Counter-Example: Unsafety is Orthogonal to Privacy Field unsafety is orthogonal to field visibility. An `unsafe` field may be `pub`, just as a safe field may be `pub(self)`; e.g.: ```rust struct NuclearBriefcase { /// Do not expose carelessly! pub(self) launch_code: [u8; 32] } ``` ## 2. Unsafe Usage is Always Unsafe > Uses of `unsafe` fields which could violate their invariants *must* occur in the scope of an `unsafe` block. ### Rationale This requirement is consistent with the requirements of the `unsafe` keyword when applied to other declarations; for example: - An `unsafe` trait may only be implemented with an `unsafe impl`. - An `unsafe` function is only callable in the scope of an `unsafe` block. These requirements are not negotiable; likewise, the requirement that risky operations on `unsafe` fields require `unsafe` should also be non-negotiable. ### Implications #### Implicit Constructors are Unsafe The implicit constructor of a struct or enum variant with an `unsafe` field must require `unsafe`. Writing, referencing or reading an `unsafe` field must require `unsafe`. #### Reading Unsafe Fields is Unsafe An unsafe field with a suspended invariant can only be read from its enclosing type if the reader respects that the value might be in an invalid state. This amounts to a safety invariant: if the value is in an invalid state, subsequent (potentially safe) uses must not require that it is in a valid state. Consequently, reading an unsafe field must require unsafe. #### Dropping an Unsafe Field is Unsafe A field with both: 1. a non-trivial drop 2. suspended invariant may not be sound to drop, as its `Drop` impl may depend on the value being in a valid state. Consequently, `unsafe` fields must either be `Copy` or `ManuallyDrop`, both of which preclude non-trivial drops. ## 3. Safe Usage is Usually Safe > Uses of `unsafe` fields which cannot violate their invariants *should not* require an unsafe block. Given that the use of `unsafe` on fields is a social contract, adherence to that social contract will depend on the UX of *using* `unsafe` fields. We should take care to minimize how often users will be prompted to use `unsafe` for field accesses that *clearly* cannot violate the field's safety invariant. ### Variants with Safe Fields Given an enum whose variants contain a mix of safe and unsafe fields; e.g.: ```rust enum Example { Safe(u8, u8, u8), Unsafe(u8, unsafe u8, u8), } ``` It should be safe to initialize and destruture `Example::Safe`, but not `Example::Unsafe`. ### Fields with Local, Non-Suspended Invariants Fields with local, non-suspended invariants are potentially always safe to read. For example, consider reading out the field `pow` from `Alignment`: ```rust struct Alignment { /// SAFETY: `pow` must be between 0 and 29. pub unsafe pow: u8, } ``` Outside of the context of `Alignment`, `u8` has no special meaning. It has no library safety invariants (and thus no library safety invariants that might be suspended by the field `pow`), and it is not a pointer or handle to another resource. The set of safe-to-read types, $S$, includes: - primitive numeric types - public, compound types with public constructors whose members are in $S$. A type-directed analysis could make reads of these field types safe. ## Design: Syntax The `unsafe` modifier is applicable to the fields of struct-like variants; e.g.: ```rust struct ExampleStruct { a: u8, unsafe b: u8, } struct ExampleEnum { UnitLike, TupleLike(u8, u8), StructLike { a: u8, unsafe b: u8, }, } struct ExampleUnion { a: u8, unsafe b: u8, } ``` It is not applicable tuple-like variants, as this would admit ambiguous parses; e.g.: ```rust struct ExampleAmbiguous(unsafe fn()) ``` ## Design: Interaction with `Copy` The `Copy` trait is, semantically, an `unsafe` trait whose safety contract is that all members must be `Copy`. However, it is not marked `unsafe` since the compiler enforces this condition automatically on all implementations. The introduction of unsafe fields creates a declaration-site `unsafe` obligation — namely, that reading is unsafe — that would not be discharged by use-site in a `Copy` impl (which has no methods that would mention the unsafe fields). To resolve this, we make `Copy` *conditionally* (un)safe: If `Self` contains `unsafe` fields, `Copy` is `unsafe` to implement; otherwise it remains safe to implement. ## Design: Interaction with Unsafe Auto Traits If a type has unsafe fields, its safety invariants are not simply the conjunction of its field types' safety invariants. Consequently, it's invalid to reason about the safety properties of these types in a purely structural manner — i.e., the manner in which auto traits are implemented. Consequently, auto implementations of unsafe auto traits should not be generated for types with unsafe fields. --- `unsafe(invalid)`

Import from clipboard

Paste your markdown or webpage here...

Advanced permission required

Your current role can only read. Ask the system administrator to acquire write and comment permission.

This team is disabled

Sorry, this team is disabled. You can't edit this note.

This note is locked

Sorry, only owner can edit this note.

Reach the limit

Sorry, you've reached the max length this note can be.
Please reduce the content or divide it to more notes, thank you!

Import from Gist

Import from Snippet

or

Export to Snippet

Are you sure?

Do you really want to delete this note?
All users will lose their connection.

Create a note from template

Create a note from template

Oops...
This template has been removed or transferred.
Upgrade
All
  • All
  • Team
No template.

Create a template

Upgrade

Delete template

Do you really want to delete this template?
Turn this template into a regular note and keep its content, versions, and comments.

This page need refresh

You have an incompatible client version.
Refresh to update.
New version available!
See releases notes here
Refresh to enjoy new features.
Your user state has changed.
Refresh to load new user state.

Sign in

Forgot password

or

By clicking below, you agree to our terms of service.

Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
Wallet ( )
Connect another wallet

New to HackMD? Sign up

Help

  • English
  • 中文
  • Français
  • Deutsch
  • 日本語
  • Español
  • Català
  • Ελληνικά
  • Português
  • italiano
  • Türkçe
  • Русский
  • Nederlands
  • hrvatski jezik
  • język polski
  • Українська
  • हिन्दी
  • svenska
  • Esperanto
  • dansk

Documents

Help & Tutorial

How to use Book mode

Slide Example

API Docs

Edit in VSCode

Install browser extension

Contacts

Feedback

Discord

Send us email

Resources

Releases

Pricing

Blog

Policy

Terms

Privacy

Cheatsheet

Syntax Example Reference
# Header Header 基本排版
- Unordered List
  • Unordered List
1. Ordered List
  1. Ordered List
- [ ] Todo List
  • Todo List
> Blockquote
Blockquote
**Bold font** Bold font
*Italics font* Italics font
~~Strikethrough~~ Strikethrough
19^th^ 19th
H~2~O H2O
++Inserted text++ Inserted text
==Marked text== Marked text
[link text](https:// "title") Link
![image alt](https:// "title") Image
`Code` Code 在筆記中貼入程式碼
```javascript
var i = 0;
```
var i = 0;
:smile: :smile: Emoji list
{%youtube youtube_id %} Externals
$L^aT_eX$ LaTeX
:::info
This is a alert area.
:::

This is a alert area.

Versions and GitHub Sync
Get Full History Access

  • Edit version name
  • Delete

revision author avatar     named on  

More Less

Note content is identical to the latest version.
Compare
    Choose a version
    No search result
    Version not found
Sign in to link this note to GitHub
Learn more
This note is not linked with GitHub
 

Feedback

Submission failed, please try again

Thanks for your support.

On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

Please give us some advice and help us improve HackMD.

 

Thanks for your feedback

Remove version name

Do you want to remove this version name and description?

Transfer ownership

Transfer to
    Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

      Link with GitHub

      Please authorize HackMD on GitHub
      • Please sign in to GitHub and install the HackMD app on your GitHub repo.
      • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
      Learn more  Sign in to GitHub

      Push the note to GitHub Push to GitHub Pull a file from GitHub

        Authorize again
       

      Choose which file to push to

      Select repo
      Refresh Authorize more repos
      Select branch
      Select file
      Select branch
      Choose version(s) to push
      • Save a new version and push
      • Choose from existing versions
      Include title and tags
      Available push count

      Pull from GitHub

       
      File from GitHub
      File from HackMD

      GitHub Link Settings

      File linked

      Linked by
      File path
      Last synced branch
      Available push count

      Danger Zone

      Unlink
      You will no longer receive notification when GitHub file changes after unlink.

      Syncing

      Push failed

      Push successfully