Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

rename 'unsafe' to 'trusted.' #117

Closed
wants to merge 1 commit into from
@steveklabnik

unsafe blocks are one of Rust's most important features. However, the name "unsafe" doesn't properly communicate the intention of unsafe blocks. I propose we change the "unsafe" keyword to "trusted," initially deprecating it.

@kballard

This seems to imply that normal users of Rust should be using unsafe blocks as a matter of course, and I disagree. unsafe should largely be used when building reusable fast components (e.g. the standard library), but most users should be using exclusively safe APIs, only needing to fall down to unsafe when they have a performance bottleneck and simply can't avoid it.

To that end, I greatly prefer the current name unsafe. Merely typing it out makes me pause, because I'm entering code that the compiler cannot verify is safe. Which means that the code inside an unsafe block may in fact be unsafe in reality, and it's up to me to prove that it is actually safe.

@huonw
Owner

I'm slightly concerned this name may get confused with "I've checked this code and trust it" (i.e. arbitrary "bug free" code), rather than "the compiler is trusting me" (i.e. dangerous code).

@steveklabnik

This seems to imply that normal users of Rust should be using unsafe blocks as a matter of course, and I disagree.

I also disagree, however, they need to know that unsafe exists. Before I mention unsafe, many people assume Rust cannot replace C or C++, because they don't think certain kinds of low-level primitives can be implemented. This is also why the 30 minute introduction mentions unsafe.

@ben0x539

Are you suggesting this for unsafe blocks only or also unsafe functions?

@bstrie

@steveklabnik, the keyword unsafe better conveys Rust's abilities to replace C/C++ than trusted.

Either way, you'll have to explain to someone that Rust can do these low-level shenanigans. unsafe is both a visual warning and a subtle discouragement. Your unsafe code is unsafe, and to imply otherwise is wildly dangerous. -1 from me.

@bstrie

That said, I'm not about to assert that unsafe is necessarily the best possible keyword for this, though it's a good one. Whatever keyword you choose has to imply unsafety rather than safety. If you don't get subtle anxiety from the presence an unsafe block, we've failed.

@ben0x539

We could make unsafe { ... } blocks into extern "C" { ... } blocks to make it absolutely clear that C-style code can be written in rust ;)

@mcpherrinm

I like the term "barrier" to describe what an unsafe block provides. On its own, I don't think it conveys the right terminology, but I'd like to at least fling it into the vocabulary mix here.

I have definitely been convinced that unsafe { } is not the right syntax. It doesn't convey at all that it's a barrier, and should be safe itself.

I don't exactly like "trusted", as it goes too far the opposite way. "unchecked", maybe is a good word.

@bstrie

I have definitely been convinced that unsafe { } is not the right syntax. It doesn't convey at all that it's a barrier, and should be safe itself.

"Should be safe" presumes optimism. Rust is not an optimistic language, it is a pessimistic one (as are all languages with strong static type systems). Your unsafe code is unsafe, and you should feel unsafe. (Sorry, I'm too lazy to track down the Zoidberg meme generator.)

@mletterle

I would propose a small change.. rather than trusted try trustme.

@crazymykl

trusted is less-than-ideal because it implies that other code (with stronger guarantees) is "untrusted".

unsafe is more similar to hold_my_beer_and_watch_this; but trustme might be more terse/appropriate.

@mcpherrinm

"Should be safe" presumes optimism.

I don't think I agree with your sentiment: If an unsafe { } block can be used to cause unsafety, then that code has bugs. We should convey the intended purpose of unsafe blocks: to denote the edge of unsafety, where the guarantees have been made.

@UtherII

IMHO, even if "unsafe" can be misleading, "trusted" is far worse. When people will see "trusted" block, they will have the feeling, that the block is safer. It's quite the opposite. "unchecked" would be closer to reality but not scary enough to convince people that it should be avoided as much as possible.

I still think that unsafe is a good choice. We can advertise better this feature without renaming it.

@brocktopus

What about something like "unwise" or "inadvisable"? If the intent is really to keep people away from its common use--but not so much as to outright prevent that use--"unsafe" may be too much of a judgment call on the quality of code written with that kind of block (i.e., an entirely unsafe practice) compared to a name that implies a standard preference?

@ben0x539

unverifiable, uncheckable?

@andrew-d

:+1: for unverifiable or unverified

@shepmaster

Adding to the madness:

(un,non)
guaranteed,verified,verifiable,checked

@thestinger

@brocktopus: It's not unwise or inadvisable to use unsafe code. It should be limited to when it's absolutely necessary for performance or to implement the building blocks for unsafe code but that still leaves plenty of reasonable unsafe usage. Writing Rust code that's comparable to the performance of C or C++ usually means making careful usage of unsafe. The common cases can be included in the standard library, but it will never cover everything. It can't just be brushed away as something that's not necessary.

@carols10cents

I barely have any context for the connotations of what the unsafe feature needs to convey, but in general I would prefer a positive phrase rather than a word starting with un/non (while still having a negative meaning). This is because code like unless !undeselected (exaggerated but I know you've seen code like this) is harder to reason about with double (triple, etc) negatives.

dangerous? scary? risky?

@polyfractal

If the name changes, I think it is important that the new name accurately describes what's going on, rather than relying on human fear. It's just telling the compiler "hey, I got this, you can go take a nap". If it also conveys that the operation is potentially risky, that's a happy benefit.

Of the suggestions, I like unchecked, unverified, and trustme the most so far. They clearly state that the compiler is trusting you to Do The Right Thing.

@Thiez

-1. I like the way that unsafe is subtly discouraging. Its use really should be kept to a minimum, and a keyword that is slightly distasteful to have in your code will help with this. A word like trusted just doesn't quite evoke the appropriate amount of dread .

I also think unsafe has a clear meaning. There are some things that are defined as unsafe in the Rust language, and they can only happen in blocks that are marked unsafe. Easy to remember, and the link between unsafe blocks and unsafety in the language spec is easily made.

Whatever keyword is chosen, I think it should clearly suggest that the code it guards is more likely to do something bad/unsafe/buggy, while something like trusted suggests the opposite.

@mcpherrinm

Unsafe does have precedence, as C# does the same thing: http://msdn.microsoft.com/en-US/library/t2yzs44b.aspx

@steveklabnik

A small survey:

Idris uses trustme, I'm inclined to change this RFC to that. ;)

Haskell's got unsafePerformIO, which isn't exactly the same thing...

OCaml has Obj.magic.

@steveklabnik

Are you suggesting this for unsafe blocks only or also unsafe functions?

This is a good point. Unsafe functions and unsafe blocks have different semantics, actually. Hrm.

@steveklabnik

Whatever keyword you choose has to imply unsafety rather than safety. If you don't get subtle anxiety from the presence an unsafe block, we've failed

While I agree, I think this anxiety can come from social pressure, rather than being super inherent to the language itself. In other words, I think that the invariants are the most important information to convey, not the details.

@steveklabnik

@Thiez

I also think unsafe has a clear meaning.

Dozens of conversations I've had with people just learning Rust disagree with you. I agree it's not hard to learn; but if people outside of Rust get confused, that's a good pointer that this is a bad name. Once you learn it, it's not that bad, but why not be clear what you mean from the start?

@steveklabnik

I actually think trustme is a much better name than trusted, and am considering updating the text to reflect it.

@dobkeratops

the existing name,unsafe{}, makes perfect sense to me. There are safe operations(which the compiler can verify), vs unsafe operations (which you must test and verify by other means).
What happens if a rust program has a segfault? you should look at the unsafe blocks. that makes sense. +1 to the comment that unsafe conveys that rust is a suitable C/C++ replacement. It reassures you that there is no 'safety net' holding you back from implementing anything you want, its' just you have to clearly mark it.

@steveklabnik

I would also like to add some context here too: I have given more Rust presentations than anyone else that I know of (6? Anyone beat that?), on three continents. I've specifically been teaching people Rust for over a year, and have taught programming as a profession for a few years. I'm not saying that means that I'm correct, just that I may have run into this more than other people have, since Rust advocacy is a large part of what I do.

I want to be clear that I think unsafe is a bad name more than I think trusted is a good one.

@Jurily

wild?

@mcpherrinm

I really like "trustme", though it doesn't entirely fit within any of rust's naming conventions. snake or camel case would be weird in a keyword though.

@shepmaster

A lot of comments describe unsafe with a more verbose meaning, other comments hint that this feature is something that most users shouldn't use. Perhaps take a page out of Nokogiri (I_KNOW_I_AM_USING_AN_OLD_AND_BUGGY_VERSION_OF_LIBXML2) or Minitest (i_suck_and_my_tests_are_order_dependent!) and spell out what the user is getting into?

  • i_assert_that_this_code_is_safe
  • the_compiler_can_not_verify_this_code

Programmers tend to hate typing out long things, so this has a neat side benefit of really making it awkward to use.

@Dr-Emann

@shepmaster, I want to agree with you, but since we market ourselves as a systems language, I don't think we can be THAT anti unsafe.

@tomjakubowski

May I suggest be_wary_of, as in:

be_wary_of { horse().and_then(praise_the_sun!()); }
@bharrisau

Parkinson's law of triviality? Could rename it safe, because you are saying that code in the block is definitely safe (even if it doesn't meet the compiler rules for safe). Or even have a this_code_is_safe block. Maybe run_with_scissors?

But seriously, if @steveklabnik is having issues most of the time he is explaining this it is probably worth checking the story behind unsafe blocks is clear. My understanding is that rustc has a set of rules for what it thinks is safe/unsafe - the unsafe bock is where we say "I'm breaking the rules, but this is still good and won't blow my foot off". From that point of view 'unsafe' doesn't seem like the right word to use, the code is still safe (or the programmer thinks so) it just doesn't meet the compiler's definition of safe.

@Valloric

This is a bad idea. Leave it as unsafe, it correctly communicates to the reader that the code in the block is not to be trusted to be safe; in other words, it's (surprise) unsafe code.

@kballard

If @steveklabnik is having issues most of the time he is explaining this, I think that's because it's a concept that's foreign to people used to other languages. I'm not convinced changing the name will make one whit of difference in how easy it is for a randomly-selected programmer to understand the concept.

@ecl3ctic

How about we keep unsafe functions and use assume_safe for blocks? The two uses of the keyword actually have very different meanings - the first means a function is unsafe to use and the second means that a sequence of operations is assumed safe in the surrounding context.

assume_safe actually reads as a directive to the compiler - "Assume this sequence of operations is safe".

Example:

unsafe fn deallocate_block(block: *mut Block) {...}

fn remove_node(&mut self, index: uint) {
    ....
    let block = self.get_block(index);
    ....
    assume_safe {
        deallocate_block(block);
    }
}

I think this makes the distinction very clear.

@dobkeratops

Programmers tend to hate typing out long things, so this has a neat side benefit of really making it awkward to use.

if you really think it shouldn't be used, isn't Rust the wrong language? Writing safe code still needs to be convenient. You have more to worry about and don't want the extra irritation of simply having to type more. You want convenience so you can get on with writing more tests & "debug code", which is the only workable solution at this level.

@bharrisau

I like assume_safe. Setting a function as unsafe is saying "you can potentially call this in an unsafe manner", putting a call in the block is telling the compiler to "go ahead and assume I'm using it in a safe manner".

@kballard

assume_safe looks to me, the reader, like "assume this code is safe". Which is the exact opposite of what I should be doing. I should always be assuming unsafe {} code is unsafe, so that way I am extremely careful with it.

unsafe is great because it's a huge red flag telling me, the reader, that I should consider this code to be unsafe and should give it great scrutiny. It also means that if I have a crash, I can just look at all my unsafe code, because there's something in there that's obviously, well, unsafe.

@ecl3ctic

@kballard You're mixing up the potential for unsafety with actual unsafety though. If an unsafe {} block isn't safe, your program has a bug. assume_safe tells the programmer / compiler - "I want you to assume this sequence of operations is safe, even though you can't verify it".

It doesn't make the blocks any less significant to the programmer. If they have a memory error occurring, then they think - "one of my assumptions must have been wrong" - and they can then go and audit all of their assume_safe blocks, to find the incorrect assumption.

@bharrisau

But the code isn't unsafe - one or more actions/operations in the block are unsafe. I don't have a problem with unsafe as the keyword; I know that dereferencing a raw pointer is potentially going to blow something up, the compiler can't help me with that so I need to make sure I have checks in place to ensure safety.

Perhaps we are suffering a cognitive bias that prevents us from seeing how someone new to the language is interpreting an unsafe block.

@dobkeratops

But the code isn't unsafe

but it might be. if I want to deliberately crash the program, I can. if i want to load a file as a raw binary blob and index it in an unsafe way, I can. unsafe correctly conveys the capabilities of the block.

@bharrisau

not_guaranteed_safe_by_the_compiler != unsafe. In my mind anyway, based on my personal definition of the antonym of safe.

@ecl3ctic

But the code isn't unsafe

but it might be. if I want to deliberately crash the program , I can.

And that's why it's an assumption. If the name is changed to reflect that it's an assumption, it's no harder to audit the code later. In fact, it's easier, because the keyword reflects precisely that you've made an assumption in your code.

@kballard

@ecl3ctic If you think code in an unsafe block isn't unsafe, you're making a pretty big assumption. In a properly-written program, it won't be. But very few programs are bug-free, and in a buggy program, any code that can crash the program can be expected to be found within an unsafe block.

I think the real problem here is that the exact same syntactical construct is conveying two opposite ideas, one to the compiler, and one to the reader. To the compiler, an unsafe {} block says "allow me to use things that you can't statically guarantee are safe, I'll be responsible for making that guarantee". Or in short, "trust me". But to the reader, this construct says "this code may be unsafe, tread very carefully!". And it's that latter concept, of conveying the meaning to the reader, that I think is the most important. Because the compiler really doesn't care in the slightest what keyword is used for this concept, it could be frob and it would happily accept that.

@bharrisau

-1 for frob. I don't like that at all.

@bstrie

Idris uses trustme

If we're going to start citing precedents, then C# uses unsafe for the same thing that we do:

http://msdn.microsoft.com/library/chfa2zb8.aspx

...as does Go:

http://golang.org/pkg/unsafe/

I think this anxiety can come from social pressure, rather than being super inherent to the language itself.

Social pressure is insufficient to curb this specific behavior. If this were a topic like indent width or brace style, then social pressure is a fine tool for encouraging conformity. But unsafe undermines the single most important principle of the Rust programming language, and is included only for practical purposes. And it's easy for someone like you and I to forget the fact that the vast majority of a language's users are not deeply embedded in that language's community, or even aware that such a community exists. For these people, social pressure does not exist.

Dozens of conversations I've had with people just learning Rust disagree with you.

This sounds like a selection bias. Have you actively sought out people who thought that unsafe was a great name for the concept that it represented? As noted above, it has a precedent in many other languages, and for those users its purpose will be immediately apparent.

I would also like to add some context here too

If we're credential-dropping, reading and replying to internet discussions about Rust has been very nearly my full-time job (I mean, really, approximately eight hours a day) for almost three years now. I have also taught Rust to all of my programming friends. Here's an unordered list of things that I've noticed beginners getting repeatedly stuck on:

  • Understanding just what the heck enums are, coming from C/C++ (which sucks, but we've already bikeshedded this extensively (and I can't afford @thestinger's hourly rate to write up his proposal for making things any better))
  • The fact that variable declarations are name: type instead of type name (but with ever more modern languages adopting this style, I think they'll come around)
  • Visibility, and our module system in general (hell, even I don't understand any of this)
  • How to map their understanding of C++/Java-style OO to traits (we could use a tutorial on this)
  • What Option is, and how to use it
  • Lifetimes ('nuff said)

I could go on, but misunderstanding the unsafe keyword hasn't registered on my radar.

Ultimately I think your goal is to make this language feature nicer to use. Which is admirable! But this is not a language feature that needs to be made nicer to use. Some would argue that it ought to be made even scarier and harder to use. Either way, it is not a feature for new users to be using.

Please, this is a language feature that is very, very, very easy to underestimate. Improper use of this feature in the wild will cause us massive embarrassment and undermine our credibility as a "safe" language. Let's not try to put a pretty face on it.

@dobkeratops

Perhaps in future rust may get more ideas like invariants/contracts, and other types of block like "trusted" may make sense. But then we could keep unsafe for what it is now: the ability to do absolutely anything, including things that really are unsafe, which some real world uses cases of C/C++ require.

Its only because the language has unsafe{}, cast::transmute .. that it can be considered a C++ replacement (unlike, say, Swift)

@ecl3ctic

But to the reader, this construct says "this code may be unsafe, tread very carefully!". And it's that latter concept, of conveying the meaning to the reader, that I think is the most important.

That's why unsafe is a bad keyword. It doesn't imply that the code might be unsafe. It implies that it is unsafe. assume_safe implies precisely what the truth is: that it might be unsafe, not that it is.

Yes it's easy to write code that isn't safe, but that doesn't mean we should confuse the contract the block is making with the potential to make a mistake. The contract is plain and clear: "I made a significant assumption here, that this sequence of operations is safe in the surrounding context".

@thestinger

It's not safe in the sense that the compiler can't prove that it doesn't cause undefined behaviour. I think we should stop using the term unsafe to mean undefined behaviour, because in the standard English dialect lack of safety just means lack of protection against bad things.

@dobkeratops

it might not be the original intention, but does it also mean unsafe in a security sense?(not just 'might it segfault').
And if you want to write code that really would be unsafe for the web, but safe for a trusted channel (like being burned on a disk for a game console or run in your own controlled environment), you could.

unsafe correctly conveys the full scope of potential use cases for the Rust language.

@thestinger

@dobkeratops: It conveys that the compiler cannot verify that the code doesn't invoke what Rust considers to be undefined behaviour. Safe code isn't vulnerable to some classes of security issues (memory corruption, uninitialized memory, data races) but it's certainly not guaranteed to be secure in other ways.

@ecl3ctic

It's not safe in the sense that the compiler can't prove that it doesn't cause undefined behaviour. I think we should stop using the term unsafe to mean undefined behaviour.

My understanding of "unsafe" is that there is some program state which can cause undefined behaviour in a sequence of instructions. If we assume that "unsafe" means "unable to be proven safe", then by consequence, any Rust program with a single line of unsafe code is an unsafe program. That's why I think your definition isn't the most appropriate (here).

@thestinger

Rust program with a single line of unsafe code is an unsafe program

The only boundary that Rust's compiler is able to draw between safe and unsafe code is that unsafe code is always at fault for undefined behaviour. Rust provides no guarantee that undefined behaviour doesn't occur outside of an unsafe block, the only guarantee is that the cause can be traced back to an unsafe block somewhere in the program.

Note that safe code can be indirectly responsible for memory unsafety, because if it's incorrect then an unsafe block depending on it being correct can trigger undefined behaviour, either inside the unsafe block or later on.

@jfager

@ecl3ctic

That's why unsafe is a bad keyword. It doesn't imply that the code might be unsafe. It implies that it is unsafe. assume_safe implies precisely what the truth is: that it might be unsafe, not that it is.

I'm not sure what this means. The English word 'unsafe' doesn't mean bad things are happening, it means they could happen. That is the exact meaning conveyed by the keyword - inside an unsafe block, bad things could happen. They shouldn't happen, b/c why would you intentionally write broken code?, but they could.

@bharrisau

Running with scissors is unsafe.

self.setSpeed(run);
scissors.move(a, b);

So transporting scissors in't guaranteed to be safe.

unsafe fn move(from, to) {}

But I can still move the scissors in a safe manner.

self.setSpeed(walkCareful);
self.holdScissorsPointingDown();
scissors.move(a, b);

I'm guessing the confusing part is when we say that those actions should be in an unsafe block. The compiler can't guarantee that I won't get distracted and start running with the scissors, so it tells me I'm not allowed to move them at all.

@ecl3ctic

@jfager "might be unsafe" as in, "we might have written it such that there are inputs which can cause undefined behaviour". My definition of unsafe is purely about how it is written, i.e. not that bad things "could happen", but that they "will happen on unlucky inputs".

@dobkeratops

"this part is unsafe, so you have to be careful". perfect, IMO. Its very easy to write code that can be crashed, from outside an unsafe block, using unsafe blocks - e.g. non-bounds checked indexing (- which graphics revolves around. )

When I encountered that in Rust, I began to ask for an 'unsafe' modifier for struct elements - but what I was told is 'unsafe' should actually considered to taint the entire module ..not just the code inside the unsafe block.

@ecl3ctic

I'm guessing the confusing part is when we say that those actions should be in an unsafe block. The compiler can't guarantee that I won't get distracted and start running with the scissors, so it tells me I'm not allowed to move them at all.

That's how it makes sense to say, "hey compiler, assume it is safe to transport the scissors here", rather than "hey compiler, it is unsafe to transport the scissors here" (since the sequence of operations does in fact allow you to transport the scissors safely, every time).

I won't keep posting here though, I've had my say. Don't want to clog this RFC with repeated opinions.

@thestinger

@bharrisau: Rust considers it unsafe to write code that may be responsible for undefined behaviour. This definition makes the unsafe keyword sensible for both unsafe functions and unsafe blocks. It's important that these use the same term, because it does the same thing internally in both cases. Good language design means not drawing unnecessary distinctions when existing concepts can be reused. It shouldn't be necessary to have two distinct keywords when this is already so clearly defined as a single concept.

@Dr-Emann

It really comes down to who your code is for. Are you telling the compiler "this is really safe, trust me", or are you saying to your coworker "this could be dangerous. tread carefully" (or "if you get a segfault, look here") I lean towards coding for people.

@ecl3ctic

@thestringer (One last comment) But the meanings behind unsafe functions and unsafe blocks are different. Unsafe functions can only be used in unsafe {} blocks. It is my understanding that unsafe blocks are intended to be the boundary of safety and unsafety, and that's why we should assume they're safe (assume_safe). If they were equal, then to flip it around, unsafe blocks could only be used in unsafe functions, and the end result is that every line in the program is unsafe! Where is the boundary of safety?

@rkjnsn

I like unchecked as it seems to clearly indicate what's going on. In the case of blocks, the code does things that can't be checked by the compiler, so it's up to the programmer to avoid undefined behavior. In the case of functions, the function does not check its input, so it's up to the caller to make sure the inputs are valid.

That said, I don't really have a problem with unsafe, and I don't like trusted at all.

@bharrisau

Rust considers it unsafe to write code that may be responsible for undefined behaviour.

Rust considers certain operations 'unsafe': calling an unsafe function, dereferencing a raw pointer, reading or writing a mutable static variable. Rust requires that any use of 'unsafe operations' occurs in an 'unsafe' block.

Just to be clear, the above is clear enough to me. I'm fine with the 'unsafe' keyword how it is currently being used. The unsafe block is just saying "unsafe operations are done in here" or "this block contains unsafe stuff".

The suggestion is that for a new user to Rust, seeing unsafe {} doesn't convey the above message. There may potentially be a better word to use, such that the majority of people will infer the correct understanding (instead of having to look it up). Unfortunately unsafe_operations_used_in_here is way too long. In the workplace we have signs that say forklifts_in_use which conveys the same meaning, shortening it to forklifts or unsafe doesn't.

That being said, none of the above suggestions are any better at saying "unsafe operations are done in here" than unsafe. assume_safe looks nice because it stands for "I'm using unsafe operations, but assume it is done safely" which is what the programmer is saying to the compiler.

@thestinger

@ecl3ctic: The meaning is the same in both cases. It means the compiler cannot validate that it is not responsible for causing undefined behaviour. The claim that there is any kind of strict boundary of responsibility drawn at unsafe is demonstrably false. Consider the addition of the following completely safe method to Vec<T>:

pub fn mostly_harmless(&mut self) {
    self.len = 42;
}

Even though the existing unsafe blocks used by Vec<T> is correct, this method makes undefined behaviour possible through the safe interface. It's not a strict trust boundary.

@kballard

unchecked is a bad name. It implies that various safety checks, such as array bounds checks, are disabled in this block, and that is most definitely not what is going on.

@bill-myers

Replacing unsafe with trusted is just wrong (it no longer conveys that the code is potentially unsafe if the programmer screws up), and the only reasonable option would be replacing unsafe with trusted unsafe (both keywords).

There's an expressiveness advantage in doing this: one could mark functions either "trusted unsafe" or "unsafe", where both could contain unsafe code, but only unsafe actually makes calls to function unsafe code (although this can be achieved equivalently by putting an unsafe block inside the function).

Maybe "trusted unsafe" could more intuitive, but it's not clear whether the additional keyword is worth it.

@o11c

I don't feel need to change it, but if unsafe isn't good enough, it should only be changed to something like evil or spatula.

@thestinger

Clearly it should be called the danger_zone and rustc can have an 8-bit Kenny Loggins soundtrack.

@ecl3ctic

@thestinger If there isn't a strict boundary at unsafe {}, then the program has a bug. From the manual:

When a programmer has sufficient conviction that a sequence of potentially unsafe operations is actually safe, they can encapsulate that sequence (taken as a whole) within an unsafe block. The compiler will consider uses of such code safe, in the surrounding context.

An unsafe block is supposed to be able to be treated as if it is a black box of safe code. If the block is not verifying that all the variables it uses are safe to use, then your program is incorrect. If you make the claim that there is no safety boundary around an unsafe block, then Rust is not a safe language and has no safety guarantees anywhere.

@steveklabnik

@ecl3ctic I really like assume_safe.

@thestinger

@ecl3ctic: It's not possible to write Vec<T> another way. Rust does have safety guarantees, they're just not as cut and dry as code outside unsafe blocks not being responsible for safety. The privacy system plays into this and safety boundaries are often drawn at module boundaries. Rust would need unsafe fields for it to be possible to draw a boundary at the edge of unsafe blocks rather than at module boundaries as we do right now.

@steveklabnik

@bharrisau

Perhaps we are suffering a cognitive bias that prevents us from seeing how someone new to the language is interpreting an unsafe block.

This, 100%.

@crazymykl

@steveklabnik @ecl3ctic assume_safe is talking to the compiler, not future programmers. I don't like that.

@ecl3ctic

@crazymykl It's talking to both. It says "hey Jim, I'm assuming this code is safe", as much as it is saying "hey compiler, I'm assuming this code is safe".

@ecl3ctic

Then if something goes wrong, it comes down to "Whoops, someone made a wrong assumption somewhere! I'd better go audit those assume_safe blocks."

@thestinger

You also need to audit all code that the unsafe blocks rely on both directly (calls inside the block) and indirectly (code with write access to fields used inside the block).

@o11c

@steveklabnik as someone whose prior languages are either always-safe (Java, Python) or assumed-safe (C++, C), unsafe was not confusing in the slightest.

@crazymykl

@ecl3ctic I read it as a command, "hey Jim, assume this code is safe".

@ecl3ctic

@thestinger Yes, Indeed.

@crazymykl I_assumed_safe could be used, but it's a little too verbose and personal. It's not much of a jump to cut it back to assume_safe.

@ecl3ctic

Or even assumed_safe. I'm happy with both.

@kballard

assumed_safe is conveying the wrong idea. It tells me that you made an assumption that what you are doing is safe, and that scares the hell out of me because you're playing with fundamentally unsafe operations. I don't want you to make an assumption. I want you to prove that what you're doing is actually safe.

@steveklabnik

proven_safe?

@Dr-Emann

I come from a fairly wide variety of languages (c, c#, Haxe, java, javascript, scala... Etc ), so my perspective may be skewed, but I had no trouble understanding the use of unsafe blocks.

@Arcterus

I don't really understand the point of this. To me, unsafe conveys the right meaning in that unsafe code does not have a large number of the safety checks in normal Rust code. Therefore, the unsafe code can be truly dangerous to use (e.g. accidentally dereferencing a null pointer, which shouldn't happen in purely safe code).

@steveklabnik

Therefore, the unsafe code can be truly dangerous to use

This is exactly why this name is bad. This is not true. An unsafe block should be safe to use.

@Arcterus

But it's not always safe to use. There can be unforeseen problems in the code. According to your logic, all C code is safe to use.

@Arcterus

I agree that the code should be safe to use, but it is not necessarily going to be safe.

@steveklabnik

According to your logic, all C code is safe to use.

Yes, all of C would be inside an unsafe block. You are declaring that you have manually verified that it is safe to use.

@Arcterus

And manual verification does not always work, which is why there are memory safety issues in C code. Hence, the C code is unsafe (potentially dangerous).

@steveklabnik

Hence, the C code is unsafe (potentially dangerous).

Again: yes, unsafe properly communicates that the code inside is not safe. What it does not communicate is that you are expected to uphold the invariants that safe Rust provides.

@NEETphreak

@steveklabnik, but that is implied. If one does not know why it is unsafe, a good destination would be the documentation for the language. Using a keyword like proven_safe would imply that it works like one big assert function, where everything is "proven safe" by the compiler, not the programmer.

@Arcterus

Every suggestion in this thread to replace unsafe has given the wrong idea, implying that the compiler has either determined that the code is safe, or that one can fully trust the code, which is not what unsafe means.

@Dr-Emann

No, an unsafe block is unsafe to use. The writer of an unsafe block should try to emulate safety, but even calling a "safe" function containing an unsafe block can crash your program.

@bharrisau

After thinking about it, I think @kballard's suggestion of frob is actually the best.

  1. It sticks out and makes you think "hey, why is this here?". That should be a mental cue to avoid the unsafe code or put some effort in to ensure it is safe.
  2. Nobody will assume they know what it means. They will look it up in the manual and see that there are certain operations that can potentially cause "unsafe behaviour" and that it is their responsibility to avoid that behaviour.
  3. It is short enough, and uncommon enough to grep easily.
@Arcterus

@Dr-Emann that's why it's not safe. When I see unsafe code, I assume that the programmer tried to make it as safe as possible (because why would you not?), but it cannot be guaranteed that the code is safe.

@steveklabnik

but that is implied.

Not to many of the people who aren't familiar with Rust that I've spoken to.

@Arcterus

@bharrisau I really don't like frob. I'm opposed to having nonsense words in the language.

@bharrisau

Hmm, could take a page out of Google's book and go with either teleport {} or goat {}. Same benefits as above - doesn't convey the 'wrong' meaning to anyone.

@o11c

@bharrisau @Arcterus that's why I suggested spatula - it already has an obscure meaning, but one that most people will have to look up.

@Arcterus

I don't like any of these names. :(

@ecl3ctic

If an unsafe block is NOT safe to use for any inputs obtained from safe Rust code, then your unsafe block is buggy. A correctly written unsafe block is safe to use, and marking the block as unsafe is telling the wrong story.

Yes, you can write an incorrect unsafe block, but that is an inherent risk of writing such code. What's important is that there is a well-defined boundary which is assumed to be safe, so that the rest of your code is a valid and safe Rust program.

Saying an unsafe block should remain unsafe {...} is putting the spotlight on the contents of the block. The spotlight needs to be on the interface. Anyone who types assumed_safe {...} into their editor knows what they're getting themselves into, and they accept the responsibility of ensuring the encapsulated code is in fact safe. The writers of the code around the block must be allowed to trust that the implementation is correct, just as users of the Rust standard libraries must be allowed to assume the unsafe code in Vec and Cell is implemented correctly.

@thestinger

@ecl3ctic: It's not possible to implement a type like Vec<T> with the requirements you're listing. The push function is only safe if the fields in the vector are sane, and a safe method could alter those.

@bharrisau

If you summarise the above you have

  • Words indicating that we are asking the compiler to trust us (assume_safe, trustme)
  • Words with no meaning attached (spatula, goat)
  • Words that indicate unsafe operations (one or more of the three) are being called (unsafe, danger_zone)

The initial problem was that people new to Rust got a bit stuck on the unsafe blocks (not necessarily the unsafe functions). I'm not sure the first or second category are any better than the current third one. And I'm not sure any word is much better than "unsafe" at indicating that unsafe operations are taking place.

Though maybe it would be less confusing if the definition wasn't as recursive - "You need to put any calls to unsafe functions in an unsafe block".

@NEETphreak

@steveklabnik,I agree it's meaning may not be entirely clear. My point is that many of these alternate keywords obfuscate an already subtle topic. Furthermore, I think a programmer who misinterprets unsafe to be in a much better position than a programmer who believes that the block of code has been proven correct for him/her automatically.

I'm not arguing that unsafe isn't flawed, but I do not feel the proposed keywords to be more suitable.

@ecl3ctic

@thestinger Please elaborate on that. The brevity of your statement isn't helpful. The official definition of Rust's unsafe {} block is that it is a sequence of unsafe operations that is safe in the surrounding context. I'm not proposing any definition change, only a renaming.

@bharrisau This is more than just making it easy for new users to understand. If we look at the discussion on this RFC, it's about allowing all of us to understand the meaning of an unsafe block better.

And I'm not sure any word is much better than "unsafe" at indicating that unsafe operations are taking place.

That is not what an unsafe block is about. That's what an unsafe function is about. Yes, an unsafe block contains unsafe code but critically, it provides a safe interface, when correctly written.

@bjadamson

+1 to this

@schmee

Here's an opinion from a Rust newcomer:

There has been a lot of things I've found confusing since first discovering Rust, but the unsafe keyword is not one of them. For me, the word trusted seems to imply that all the code outside these blocks is "untrusted". On the other hand unsafe implies exactly what it is supposed to: that code outside of these blocks is (memory) safe under all circumstances. I fail to see how the meaning of this is conveyed any better by nonsense-words like frob or spatula. In short, -1, keep unsafe.

@Arcterus

@schmee I fully agree, -1.

@brandonson

My $.02, coming from Java/C++/Scala/Haskell, unsafe was entirely clear. When I started with rust I essentially read it as "This code can avoid the soundness checks done by the compiler if needed. This is not a safe thing to do." It's a little more complex than that but it certainly gets the idea across, so I think unsafe should stay.

Someone also mentioned that many questions about unsafe could be due to the feature set of Rust being generally unfamiliar. With that in mind, I think that if a change is to be made, we should make sure there's another motivation beyond 'it gets lots of questions and isn't entirely clear'. I teach people who've never programmed before and get a lot of questions about terminology we take for granted, and more often than not it's unfamiliarity that's the problem, not a lack of clarity in the terminology itself. Sometimes keywords will be somewhat unclear, if only to avoid typing verbose 30-character keywords everywhere.

@ecl3ctic

@brandonson That's not what an unsafe block is! If the idea being conveyed is "operations inside aren't checked by the compiler", then that validates the problem we are trying to address. The fact that several people opposing this change don't seem to fully understand unsafe blocks is a clear reason to rename them.

@bbqsrc

unsafe is already quite clear on its purpose.

The only alternative phrasing in English I can even think of that compares is unchecked, but it doesn't have the benefit of the emotional connotation that unsafe is bad. Unchecked is neutral.

:-1:

@NEETphreak

I haven't seen wrap_unsafe from the RFC discussed much in the comments, but that's actually grown on me. I don't feel like it implies much that it isn't.

@brandonson

@ecl3ctic Nor is that what I said. I said the soundness checks can be avoided within an unsafe block if needed. Which is entirely true, using things like raw pointers and std::cast::transmute.

@ecl3ctic

@brandonson Again, that is not what an unsafe block is about. You can read the official definition of an unsafe block here:

http://doc.rust-lang.org/rust.html#unsafe-blocks

or read the earlier comments where I (and others such as Steve) have explained it several times.

@tbelaire

I was actually just going to post http://doc.rust-lang.org/rust.html#unsafety myself.

Unsafe operations are those that potentially violate the memory-safety guarantees of Rust's static semantics.

And about unsafe blocks:

When a programmer has sufficient conviction that a sequence of potentially unsafe operations is actually safe, they can encapsulate that sequence (taken as a whole) within an unsafe block. The compiler will consider uses of such code safe, in the surrounding context.

Since humans are not perfect, such blocks of code are less safe than regular code.
Thus calling them unsafe is reasonable.

I also like trustme or wrap_unsafe, since those better imply the fact that blocks turn unsafe operations into safe ones (from the compiler's point of view).

@Arcterus

@tbelaire I was just about to post that (except I don't particularly like trustme and wrap_unsafe).

@ecl3ctic

I like wrap_unsafe almost as much as I like assumed_safe. I would be happy if we ended up with wrap_unsafe.

Edit: actually I take that back. It's better than nothing, but it doesn't clearly describe the contract the programmer is making with the surrounding code. It really needs emphasis that the surrounding code should be able to treat the block as safe. assumed_safe does precisely that.

@Arcterus

In my opinion, wrap_unsafe looks too much like unwrap.

@bharrisau

If you want to make it explicit what is actually going on you can move to a lint like system

#[allow(deref_raw, mut_static, unsafe_fn)] {}

Because that is all that is happening - by default any of the above three will cause a compile error. The unsafe block is only there to make it explicit that these things are meant to be going on. The Rust manual specifies that the programmer must provide a safe interface (avoiding unsafe behaviour) when using it, but there is nothing in place to guarantee this.

@tbelaire

@ecl3ctic
Just to make sure I do understand, since I'm a Haskell guy: unsafePerformIO is like an unsafe block.
A wrapped version of libc's sine that uses unsafePerformIO to convince the compiler that it is actually a pure function is the same as a "safe" rust function that uses a unsafe block to wrap the ffi to get libc's sine function.

So it's unsafe in the same was unsafePerformIO is, since it turns a function marked as potentially performing side effects or breaking static guarantees as safe for use by the rest of the system.

@ecl3ctic

@bharrisau That's orthogonal to the issue at hand. Unsafe blocks are ______not______ about the unsafe code as much as they are about the safe interface. Writing them as a lint says nothing about the safety contract. And yes, there is no guarantee the unsafe block is written correctly, but that's because there can't be. The programmer needs to guarantee this. This is why it needs to be marked as an assumption.

@tbelaire Yes, that's correct. It's about denoting something that could be unsafe as safe in actuality.

@bharrisau

Unsafe blocks are ______not______ about the unsafe code

@ecl3ctic I'm not understanding you. The only thing an unsafe block actually does is stop the compiler from emitting an error when you do one of the three unsafe operations. The only safety contract it provides is explicit visibility that a programmer has turned off the safety interlocks. "Hey, I'm playing with matches over here".

I'm not suggesting we move to a lint, because that won't imply that the programmer needs to do anything more than "turn ff the annoying error messages".

@tbelaire

I'm not sure how what I've posted is different from brandonson's

This code can avoid the soundness checks done by the compiler if needed. This is not a safe thing to do.

@ecl3ctic

@bharrisau Yes, the only action the compiler takes is to permit unsafe operations, but the intent of the unsafe block is to encapsulate unsafe operations safely. If you fail to do this, the unsafety propagates and your entire Rust program becomes unsafe.

The compiler would enforce a safe interface if it could, but it can't, because of the nature of the unsafe operations, so that component is left to the programmer.

Essentially, both components of the unsafe block need to uphold their respective responsibilities in order for the block to be valid. The compiler needs to allow unsafe operations, and the programmer needs to guarantee the block is safe in the surrounding context.

@brandonson

@ecl3ctic More verbosely, I read unsafe as "The compiler cannot verify that these operations are safe. This means I am performing operations which could be unsafe, however, I believe and/or have proven they are safe in this context."

I'm not entirely convinced by your argument that unsafe blocks provide a safe interface. That is the job of a safe function wrapping the unsafe block. The unsafe block is there to say "I'm doing something unsafe" similar to an unsafe function, which says "I do dangerous things, don't call me unless you really know what you're doing." It's wrapping an unsafe block in a safe function which provides an interface that says "Yes, this is generally unsafe, but this is a safe context to do it in."

@bharrisau

I believe and/or have proven they are safe in this context."

@brandonson Slight nitpick of the wording. "I believe this will not cause unsafe behaviour". Where Rust defines exactly what unsafe behaviour is.

@ecl3ctic

@tbelaire The important part is that the interface is safe. I'm starting to repeat myself now, but essentially yes it allows you to do unsafe operations, but that is not the whole story, you also need to encapsulate them safely.

@brandonson I don't believe an enclosing function can be the boundary. The block is the boundary. If the block isn't the boundary in a particular piece of code, then the block should be expanded to enclose the entire function body. An unsafe block needs to act as a black box - it should be able to be refactored as a safe function.

If you didn't take the block as the boundary, then it wouldn't be clear exactly where safety begins and ends. If you have a 3 line unsafe block in a 1000 line function, is the whole function unsafe? Or just the few operations around the unsafe block. If so, how many? It's not well-defined.

@brandonson

@bharrisau Well, that amongst other things, as hopefully it will be causing correct behaviour and not just 'not unsafe'. Fair enough though, that would be more clear.

@ecl3ctic

@brandonson To address the wording of the manual:

When a programmer has sufficient conviction that a sequence of potentially unsafe operations is actually safe, they can encapsulate that sequence (taken as a whole) within an unsafe block.

It does first mention that the sequence that you're encapsulating must be safe in its own right.

@thestinger

@ecl3ctic: It's not currently possible to contain it that much no matter how much effort you put into it due to the lack of unsafe fields though. It's not possible to define the current semantics that way. The writing in the manual is a very idealized view not mapping to what's actually available in the language. The Vec<T> example I posted above is an example of why it's not possible to view the current system as working that way.

@brandonson

@ecl3ctic The sequence of potentially unsafe operations is not necessarily safe in its own right, nor does the manual quite say that. It simply says that the sequence of potentially unsafe operations is safe, which is always going to be dependent on context. (This is more clear based on the rest of this comment, IMO)

The operations around an unsafe block/sequence of operations should indeed be safe. The problem is, the block itself is not necessarily safe to use anywhere. For example, I can't put an unsafe block which frees the memory of a custom allocator wherever I want. I can only put that block in a method where I own the allocator and all memory associated with it.

For an interface to be safe, it should be impossible for whatever is behind the interface to cause unsafe behaviour, no matter what context the interface is used in. So, an unsafe block doesn't provide a safe interface, as there are contexts where certain unsafe blocks could definitely cause unsafe behaviour. That's why we need safe functions that wrap unsafe blocks, as they provide context where there will be no unsafe behaviour, and can therefore provide a safe interface.

@ecl3ctic

@thestinger I'm not sure of where there are unpluggable leaks; an example/elaboration would be nice. You mentioned something about Vec earlier without elaboration as well. I need more details in order to be able to understand your point.

If the unsafe block assigns to fields or variables which are to be read by safe code, it must ensure they are valid values. If you're talking about aliasing/mutability rules being broken within an unsafe block, then I suppose it has responsibilities not to make the effects visible in safe code (unless those effects are actually safe, like with Cell, in which case it doesn't matter).

I'm not an expert on safety rules though. I'm happy to hear about how unsafe blocks can't actually have safe interfaces (but that transitively makes all Rust programs unsafe!). I just believe I have a clear picture of the intent of the current unsafe blocks, so I'm evangelizing communicating their purpose better with a more appropriate name. The comments by a few (not the majority) of the people in this RFC suggest that even amongst us here there is confusion.

@brandonson

@eclectic The Vec<T> example @thestinger gave was based on changing the length of the vector to 42 without changing the actual values in a vector. Off the top of my head, Vec's have two fields: ptr (points to where the actual data is) and len (how many elements it has). There may be more fields, but they're not important in this case.

Consider this: I have a Vec with a length of 3. Because we don't have unsafe fields, I can have len = 500 in safe code. I can then read ptr[499] from safe code as well. However, if I've changed len without changing ptr, I will either get invalid values or cause a segfault, all without using unsafe at all.

@ecl3ctic

@brandonson I've said this many, many times now, but if your unsafe block is written such that it's NOT safe in the surrounding context, then it's incorrect!

unsafe {
   libc::free(ptr);
}

is a bug.

If you want a function that frees memory, then you want:

unsafe fn free_memory(ptr: *mut c_void) {
    libc::free(ptr);
}

An unsafe function does not expose a safe interface. Functions that do not provide safe interfaces must be unsafe functions, not functions with unsafe blocks inside.

So if you want to free the memory safely, you have to use the free_memory function in a safe context. This safe context should be wrapped in an unsafe block to inform the compiler of the safe boundary.

@ecl3ctic

@brandonson But if len is 500 in safe code, then that means you've already hit undefined behaviour because a previous unsafe block has done something wrong. The bug is back in the unsafe block; the side effects of that bug are what is appearing in the safe code.

@brandonson

if your unsafe block is written such that it's NOT safe in the surrounding context, then it's incorrect!

@ecl3ctic Of course! My point is that a block may ONLY be safe given the surrounding context. However, I think we can agree that for an interface to be safe, it must be safe to use in ANY context. (Where using it passes compiler checks, of course). Since I can't use an unsafe block in any context, it follows that the block does not provide a safe interface.

@brandonson

@ecl3ctic With respect to the Vec/len example, I think I worded it poorly. I meant that len can be set to 500 within safe code, which then allows me to read beyond the actual end of the vector, without causing any of the checks surrounding the unsafe code to fail. (i.e. I can read ptr[499])

@ecl3ctic

@brandonson But you can use an unsafe block in any context safely. If it's operating on pointers which might be null, you can check for nullity. If it's freeing memory, then the pointer to the memory might be stored in a struct which can only be created by a function with a safe interface and the struct might tag itself once the pointer has been freed.

How can Vec's len be set in safe code? It's private for users of the library. Actually, I suppose struct fields are public within a module, but then Rust needs a stronger form of privacy (or unsafe fields, yes). That's a problem with the language specification, not unsafe blocks!

Edit: I suppose I wasn't really clear in my first paragraph, but basically I disagree that unsafe blocks can't be used in any context. If they can't then they're written wrong.

I'm going to stop polluting this RFC now... I think I've argued for long enough. I've got work and stuff to do :P

@brandonson

How can Vec's len be set in safe code? It's private to users of the library. Actually, I suppose struct fields are public within a module, but then Rust needs a stronger form of privacy. That's a problem with the language specification, not unsafe blocks!

@ecl3ctic Well, that was the problem being discussed. The more general point is that there are fields which are not safe to set unless you're being careful and setting other things as well.

As for unsafe blocks, yes, you can write them to be safe in any context, but why do that when some of your checks can be done by the compiler in safe code? In your second example, why use a tag to indicate the pointer has been freed when the compiler can tell me I have ownership of the object? It's much more likely that a tag will be forgotten somewhere than it is that the compiler will be wrong!

@ecl3ctic

In your second example, why use a tag to indicate the pointer has been freed when the compiler can tell me I have ownership of the object?

That's not an argument about unsafe blocks so much as it is about my example (which is poor). This is way off track.

My stance in this RFC is that assumed_safe is a more appropriate word for an unsafe block because a correctly-written unsafe block must guarantee a safe interface! This is my point, which has been progressively washed away. Yes there might be loopholes in Rust's safety system, and we should work to resolve those. An unsafe block really should have its name describe the contract it is making with the enclosing context (even if there are currently loopholes). The whole point of an unsafe block is to provide such a contract. The fact that unsafe blocks contain unsafe operations is a comparatively minor detail and certainly not the complete or most appropriate definition. It becomes important only when a memory error occurs and you need to audit their implementation.

Alternative names:
promised_safe
deemed_safe
trusted_sequence

With that, I type no more replies to this RFC. I'm sure everyone thinks I've said enough.
Edit: Okay, I made another reply. But I'm not arguing with anyone.

@pczarn

A function should be marked as unsafe when it performs unsafe operations for any set of arguments. For instance, dereferencing a raw ptr is safe for some pointers but not others. A block such as unsafe { *raw_ptr.offset(n) } is correct and can be assumed safe only if it reads a valid location in memory and doesn't cause undefined behavior for all raw_ptr and n. This can be ensured by the programmer with assertions or deducted from code within the same module.

Unsafe functions and operations should be composed in a way that can be trusted to be safe within an unsafe block. Otherwise, to write idiomatic code you would have to mark functions as unsafe all the way up to main, if my understanding is correct.

@glaebhoerl

Based on @thestinger's tongue-in-cheek suggestion, and as @carols10cents also floated, I like dangerous. It's basically perfect:

  • It avoids the ambiguity of "unsafe", which can mean either "is bad", e.g. an invalid memory access, or "has the potential to be bad", e.g. the ability to perform invalid memory accesses. (And which will probably end up being employed in both senses in attempts to explain the meaning of unsafe.)

  • It also avoids the ambiguity of unsafe where it may not be obvious that it refers to the inputs, the individual operations, and not to the output, their composition, which should be safe.

  • It captures the correct meaning, "this could go bad", rather than "will necessarily go bad".

  • It has a suitably negative connotation, unlike trust, trusted, trustme, etc.

  • After hearing "dangerous", the next thought anyone has is "be careful", or perhaps "avoid", which are exactly right.

  • A potential disadvantage is that it's a longer word, but that also might be an advantage.

  • The only definite disadvantage is the divergence from other languages, but given the self-explanatoriness of the word, I'm not sure that this should be an overriding concern.

@SiegeLord

The way I resolved this double-speak in my head was that 'safe' in 'guaranteed memory safety' meant that there were no reads of uninitialized memory, invalid writes etc, while 'safe' in 'unsafe {}' meant that that code wasn't provably safe. I think those are related, but definitely different meanings. You can't verbally say "the code in the unsafe block is unsafe" and use the first meaning for the second 'unsafe' because that's not always true... in fact, a properly written Rust program has no unsafe code even if it has unsafe blocks (essentially using the process @pczarn described).

The right meaning of 'unsafe {}' seems to be 'this code is potentially memory unsafe'. To that end, I like the prior suggestions of 'danger_zone', 'dangerous', and I'd add 'maybe_unsafe'.

@shepmaster

Continuing to extract and distill what I hear other (more knowledgable) people saying, what about these variants:

  • {programmer,human}_verified, verified_by_{human,programmer} - these denote that you are taking on a responsibility normally reserved for the compiler.
  • safe_in(_this)?_context - codifies that this sequence of unsafe calls, in this context is safe.

My snarky addition would be:

  • no_better_than_c - indicates that you are leaving (some of) the goodness of Rust behind.
@Dr-Emann

I disagree with the premise that unsafe{} should be a safe interface, or be assumed correct from the outside.

An unsafe{} block is not a black box. A safe function, wrapped around an unsafe block is where one can assume safety. It is the duty of both the surrounding 'safe' code, as well as the internal unsafe code to be sure that the combination thereof is safe. It is perfectly valid not to check that a pointer is non-null before dereferencing, iff the surrounding safe code can ensure the unsafe block is never called with a null pointer. An unsafe block is only safe to use in a particular environment, and thus, one cannot treat it like a black box: i.e. changing the safe code surrounding an unsafe block is unsafe.

It would make more sense to annotate the functions containing unsafe blocks with assume_safe or trustme, not the unsafe blocks themselves (I don't actually think we need to do this)

I really think "unsafe" is fine, but if we really want to change the name, I can only agree with suggestions that do not imply that unsafety ends at the boundaries of the unsafe block.

@steveklabnik

@ecl3ctic thank you for all your comments while I slept.

@steveklabnik

I got some feedback from Andrei over on Reddit: http://www.reddit.com/r/programming/comments/28005n/rust_rename_unsafe_to_trusted/ci6978j

The combo safe/trusted/system has worked well for D (I think you need all).

@nathanaschbacher

It seems to me that unsafe is precisely what these code blocks should be called because they can violate the safety guarantees that Rust provides.

If you change those blocks to be called trusted, then that's just doubly confusing. First because it just linguistically makes no sense... those blocks violate Rust's safety semantics. If you change it to "trusted", then I'd suggest changing the descriptors of Rust to include "type trustworthiness", "memory trustworthiness", etc. etc. etc. Then secondly the term trusted implies the exact opposite of what one should take away from those code blocks. Those blocks are to be explicitly untrusted because they can do basically anything.

@jfager

People arguing in favor of this change seem to be conflating 'safe' and 'correct'. They are orthogonal concepts. The English words "safe" and "unsafe" describe the imminence of harm; being unsafe does not imply that harm is actually happening, only that it is more likely to happen than in a safe alternative. That exactly describes the feature, and unsafe is exactly the right word for it.

The refrain that's being repeated is that when someone uses an unsafe block, they are asserting the code inside is safe. But that's not really true; it can't be, they've just explicitly turned off some of the language's safety features! They are actually asserting the code inside is correct, despite the reduced safety. The word 'unsafe' serves as an acknowledgement of the increased imminence of harm from the reduced compiler verification in that block.

As far as making an assertion that a piece of code is safe for other people to use, that's accomplished by exposing a nominally safe fn around an inner unsafe block. The right word for this isn't in question, because there is no word for it; a nominally safe fn is indicated by the absence of a marker, not the presence of one.

Anyways, -1.

@thestinger

@ecl3ctic: You can't really say it's incorrect when it's impossible to write an equivalent to a type like Vec<T> without an unsafe block depending on the surrounding unsafe code. How do you propose that push be written if it can't depend on the fields in the vector being valid?

@ian-p-cooke

'unsafe' means 'harm may occur because there is no protection' (in English and Rust). Something safe is unlikely to cause harm because it is protected but it is not guaranteed to cause no harm.

Consider what protection is lost and what is providing that protection. The safety we're talking about is that provided by the language/implementation. If a developer who wrote the code verified it would not cause an invalid memory access in some way is irrelevant; the language/implementation does not protect against that code and because there is no protection it is unsafe.

the keyword does not imply anything about the surrounding context nor is it intended to. it's a block annotation and as such describes the code in the block. The protection that Rust provides is regained after the end of the block. That doesn't imply that you won't access invalid memory at some later point while executing code from outside an 'unsafe' block; it just means the code outside the 'unsafe' block didn't cause the error. The code was a victim of unsafe behavior. Executing safe code is unlikely to lead to harm but it can not guarantee it.

'trusted' describes why it's ok to use some unsafe operations. 'Why' doesn't matter. It's still unprotected by the language/implementation. Actually, maybe 'unprotected' would be more clear than 'unsafe' to some people.

If people new to Rust are confused I'd point at the documentation itself: "When a programmer has sufficient conviction that a sequence of potentially unsafe operations is actually safe, they can encapsulate that sequence (taken as a whole) within an unsafe block. The compiler will consider uses of such code safe, in the surrounding context." The code isn't 'potentially unsafe' it's 'potentially harmful'. The compiler doesn't 'consider the code safe' it 'permits unsafe operations in the code'.

@nikomatsakis

So, I actually have been working on an alternate RFC in which I really wanted the keyword "trusted". I will try to clean it up and publish it (rather than describing here). But suffice to say, I am not opposed to changing from "unsafe" blocks to "trusted" blocks. (I slightly prefer the adjective form to trust for my other use case, and both seem to work ok for code blocks)

@ecl3ctic

@thestinger I'd so as far to say that Vec (and other code) is using unsafe blocks poorly. Since push() is only safe when you consider the function as a whole, the whole function should really be wrapped in an unsafe block. But that might make it too unclear which operations are the unsafe ones.

I think if Rust supported something like my proposition below, the distinction between unsafe operations and (manually-assured) safe interfaces would be clearer. At this point I believe there is an unnecessary and burdensome coupling between the two.

My proposition:

1) The unsafe block has its meaning changed such that it denotes nothing more than "unsafe operations can be performed within this block". Unsafe blocks propagate their unsafety out to the whole function, meaning they won't be accepted in any safe function.
2) Tagging a function as unsafe tells the compiler to permit unsafe code to be present in the function. It will be an error for a safe Rust function to include an unsafe block or unsafe function (unless (3) is present). As is currently the case, unsafe functions may only be called within unsafe blocks. The change from the current behaviour is that the tag on the function does not say "you can write any unsafe code you want here" but rather "this function contains unsafe blocks, and is consequently unsafe itself". It does not imply that unsafe code can be written anywhere in the function.
3) To end the propagation of unsafety, a group of unsafe blocks is wrapped in a safe_interface [1] block, which has only one meaning. It means "I guarantee that the code within this block is a safe sequence of instructions, no matter the context in which it may reside". If written correctly, it is a black box of safe code. The compiler treats it as such. Like with our current unsafe blocks, the burden of correctness is on the programmer. If something goes wrong, the safe_interface blocks are audited to find the mistake.
4) To ensure structs such as Vec can't be messed with in safe code by making some invalid change to its fields (such as changing len to be larger than the underlying memory) we add an unsafe keyword for fields, which allows them to be modified only in unsafe code. For Vec, all of its fields should be marked as unsafe, since they can't be changed independently.

These changes would decouple the unsafe instructions from the safe interface, and make it clear where the programmer-assured safety boundary is. Unsafe blocks become truly unsafe [2], all the time, and the programmer's safety promises are put behind a safe_interface block.

I'm not sure if this is something like the RFC you're cooking up @nikomatsakis

[1] As an alternative to safe_interface, I also like trust_safe. I think it might even be nicer, because it clearly denotes that we are only "trusting" that we expose a safe interface. promise_safe_interface might also be a good choice. It clearly states that this a promise from the programmer to the compiler and other programmers. Another alternative is trust_independently_safe. These last few keywords are verbose, but if they're being written everywhere, someone is doing something very wrong! Maybe they could be expressed as attributes instead, e.g. #[promise_safe_interface] {...}, since the keywords are really about informing code readers (including Bob, Tracey, and the compiler) about a property of the code rather than denoting language constructs.

[2] There are two kinds of "safe" and "unsafe" that have arisen in this thread and have ultimately been mixed about. One is "unsafe to write". Our current unsafe blocks are unsafe to write, because if the programmer does something wrong, we can get memory errors and undefined behaviour (UB). However, the current unsafe blocks are supposed to be "safe to execute", that is, if written correctly they should not cause memory errors or UB with input generated by safe Rust code. My proposed new unsafe blocks are both "unsafe to write" and "unsafe to execute" - they aren't supposed to give any guarantees. safe_interface blocks give the safety guarantees - they are "unsafe to write" but "safe to execute".

Example of the proposed system in action (choosing trust_safe as the keyword):

struct FooBar {...}
struct Schazam {...}

// This function contains unsafe blocks without declaring a safe interface,
// and therefore must be declared unsafe itself.
unsafe fn bar(fb: &mut FooBar) -> *mut Schazam {
    < do operations which are *safe* on their own >
    unsafe {
        < do operations which are *unsafe* on their own >
    }
    < do operations which are *safe* on their own >
}

// This function must be declared unsafe for the same reason.
unsafe fn wop(sc: *mut Schazam) {
    < do operations which are *safe* on their own >
    unsafe {
        < do operations which are *unsafe* on their own >
    }
    < do operations which are *safe* on their own >
}

// This function contains unsafe blocks, but declares a safe interface,
// and therefore we trust it to be a safe function. It will act like any other
// safe Rust function, providing it is implemented correctly.
// If not, we know where to look: `grep trust_safe`.
fn foo(fb: &mut FooBar) {
    < do safe things >
    // We declare here that the contained unsafe blocks, although
    // independently unsafe, are safe when performed in this sequence.
    trust_safe {
        unsafe {
            let sc = bar(fb);
            < do operations which are *unsafe* on their own >
        }
        < do operations which are *safe* on their own >
        unsafe {
            wop(sc);
        }
    }
}

We do several (unspecified) unsafe things here, but the programmer makes an assertion that we can trust the sequence of operations to be safe overall, for any input generated by safe Rust code.

Keep in mind that, as I talked about earlier (see [2]), there are two kinds of "safe" - "safe to write" and "safe to execute". When we have a safe_interface / trust_safe , we are talking about "safe to execute". This kind of code can't be safe to write - that's what ordinary Rust code is for!

@thestinger

I'd so as far to say that Vec (and other code) is using unsafe blocks poorly. Since push() is only safe when you consider the function as a whole, the whole function should really be wrapped in an unsafe block. But that might make it too unclear which operations are the unsafe ones.

I think you're missing the issue here. It wouldn't make a difference if the entire body of the function was wrapped in an unsafe block because it still depends on exterior code being correct. The push function is only correct if the fields in the struct are set properly, and entirely safe methods can modify those. Safety is enforced at module boundaries in most cases.

@ecl3ctic

@thestinger That's why I made (4) in my proposition. I didn't miss that point (but I did yesterday).

@nathanaschbacher

Considering the other stuff that needs to be done, the inevitable confusion this change would create, and the essentially zero practical gain that will come from making it... this seems like almost the epitome of bike shedding.

@jcdyer

I suggest keeping it as unsafe, for one major reason: Rust's documentation as well as articles and talks about rust all talk about rust being a safe language. It provides memory safety. It lets you write systems level programs more safely than the incumbent players (C & C++). Trust has a different set of meanings. With trusted or trustme, users will not only have to figure out why they would want to use that keyword, they also have to figure out if trust is the same thing as safety, if it's a vaguely related term, or if it's an entirely separate concept in the language. If you tell everyone that rust makes safety guarantees, and then give a tool to mark things "unsafe," it makes sense that the unsafe keyword is somehow related to language safety. Trusted or trustme might be about something entirely different. It adds cognitive load for the learner that doesn't need to be there.

Some supporting data:

The term "safe" comes up 3733 times in the documentation. Eliminating references to the unsafe keyword (grep -v "unsafe"), it still shows up 1855 times.

The term "trust" only shows up 185 times.

@alexchandel

Rust is about safety, and unsafe blocks allow one to bypass the memory safety guarantees of the compiler, potentially writing untrustworthy code. -1 for trusted, because that reads like the code block should be trusted to execute correctly (which it shouldn't!)

I'd favor a more nuanced term that expressed exactly how the block was unsafe. Perhaps unprotected, which implies that the code block isn't protected by something (the compiler's memory guarantees, here).

@steveklabnik
Owner

because that reads like the code block should be trusted to execute correctly (which it shouldn't!)

Yes, it should.

@alexchandel

Yes, it should.

I think being in a trusted block implies that the code ought to be trusted by the programmer to execute safely, by virtue of being in a trusted block. I don't think that it connotes a command to the compiler to trust the code. However, unprotected reads, from the point of view of both the programmer and the compiler, that memory safety protections are not applied to the code block. Similarly, unsafe reads that there is a lack of (memory) safety in the operations performed in the block.

@UtherII

Yes, it should.

It should trust the author of the code, but I expect to find what the author grant me in comments, or maybe in annotations, not language keyword.

In all languages I know, the language keywords are about what the compiler grant. So if I see a "trusted" keyword, I would naturally think that the compiler provide additional safety, while it's the opposite.

@alexchandel

not language keyword.

That's my thinking.

So if I see a "trusted" keyword, I would naturally think that the compiler provide additional safety, while it's the opposite.

Although I'm beginning to see it, I'm not entirely sure what to think about a code block called trusted. I suppose it's intended to emphasize that it's a trusted node in the syntax. But all code that compiles is trusted; there is no block of code that could be marked untrusted.

So what about just trust? It tells the compiler to trust otherwise untrustworthy code (raw pointers), and it's neutral with the regard to the user. It doesn't suggest the writer ought to trust this code (unlike trusted), it still implies that the compiler is unable to prove that no undefined behavior will result (unlike trusted), and it doesn't suggest that potentially safe code is bad (unlike unsafe).

That or trust_that_no_undefined_behavior_occurs_despite_raw.

@jcdyer

So what about just trust?

I still prefer unsafe due to its clear connection to the concept of safety, but trust handles most of my concerns with trusted. The word trusted implies that the code doesn't have any problems in it, while the truth is that if there are memory safety issues, they are guaranteed to be precisely in the trusted part, so it feels like we're lying to our programmers. Trust implies more uncertainty, which accurately reflects what's going on in the code. The writer believes the code is correct, but that the only safety mechanism is trust. The implication is not that the code is truly worthy of trust, but that it is asking for trust (conditionally, until bugs are found).

+0: If there's a strong will to change the syntax, this seems like a good choice.

@thestinger

I think changing unsafe for blocks but leaving it the same for functions will be much worse than the current naming scheme. The meaning of unsafe for functions also doesn't mean that calling them is unsafe, it just means the compiler can't verify that calls to them are safe (just like unsafe blocks - at the moment it's consistent). If blocks use trusted, then functions should really use something like untrusted or it won't have a consistent overall design. I prefer leaving both a unsafe, meaning the compiler can't verify the safety (which is by definition unsafe, and unsafety does not imply incorrect at all).

@steveklabnik
Owner
@thestinger

Both unsafe functions and blocks permit the same things (dereferencing unsafe pointers, calling unsafe functions) in the contained code. They both define a section of code where the programmer is responsible for safety. The term unsafe means lack of safety, not undefined behavior. It implies that the programmer is now responsible for preventing undefined behavior. An unsafe block is a safety boundary, but it has an internal meaning rather than just the external one. It defines a boundary between safe and unsafe code, and I think unsafe is a far better term for that than trusted, especially since it's consistent with unsafe functions where it has the same meaning in a slightly different context.

@sinistersnare

I dont think we should use trusted because we should not trust unsafe blocks, we need to scrutinize them.

If I had to change it, I would say that it should be called unchecked which is exactly what it is, not checked, so we need to do the checking manually.

if we use the word trusted or proven it sounds as if we know that it is just as safe as any regular block, which it is not. Unsafe is used because it alerts the programmer to double check everything in the block. Unchecked, I feel, is more correct than unsafe, as it does not imply bias, and it alerts the programmer accordingly.

And on a tangent, the quote is wrong in the "motivations" section:

This code is not able to be determined to be safe by the compiler, but I promise you that it is.

We should never 'promise' that it is, because that could be misleading. The compiler makes us promises, it knows what it is doing; we may not know what we are doing, and we should not be making promises like that.

But for unsafe, it has precedence with Haskell at least, so -1 to trusted, +1 to unsafe, and +2 to unchecked

@alexchandel

@jcdyer Exactly, it has all the right connotations.

@steveklabnik

No, unsafe functions are different: you cannot use an unsafe function without also wrapping it in an unsafe block.

That's a great reason to call functions untrusted. Why not call the blocks trust then, to parallel this?

Edit: posting error

@errordeveloper

How about suspicious, dangerous or even doddgy?

Looks like this discussion has more points towards not renaming actually. I don't seem to see any reasonable argument here on why it needs renaming. It's a pretty keyword and does indeed associate with what it means.

May be syntax needs to be made more horrid, like make user have to prefix each new line with a ! or something of that sort?

fn foo() {
!! hello();
!! and_die(void);
}

Or, actually, may be requiring them to type the attribute like this instead?

fn foo() {
#[UNSAFE]      hello();
#[UNSAFE]      and_die(void);
}
@errordeveloper

By the way, has anyone attempted to propose --screw-the-safety-rules-i-am-a-masochist flag yet? If not, sure someone will, may be as a side effect to some not so obviously named flag... I'll bet.

@errordeveloper
commit f947b3f0b8e5d08cb4e677419d868ee8d641f55d
Author: Joe Blockes <jblockes@google.ie>
Date:   Fri Jul 13 21:27:29 2017 -0000

    Add `--i-am-feeling-lucky`
@raindev

I'm new to Rust. unsafe is clear but scary a little bit. trustme is fun, I like that.

blackhole
Clearly denoting that we are reaching somewhere beyond where rustc could look and keep us safe and warm. …where time and space doesn't exists and our program could collapse :P

I see analogy between unsafe and Java'a concept of checked exceptions. It's like unsafe function make context calling it unsafe. Using unsafe block you're getting back that assumption of safety, keeping it in you local context, unlike unsafe function. To summarize the difference is all about if safety could be guaranteed in a current context.

made_safe (unsafe) block shields us from unsafe functions. That's it.

@ecl3ctic and @brandonson made great points on that differences explanations.

@liigo
@steveklabnik

I am closing this RFC. I still think that this is a poor name, but I plan on seriously revising this RFC anyway, and the discussion here will largely be irrelevant.

I will open a new RFC when I have a better plan. Thank you all.

@pczarn

You're welcome. IMO trusted fn / trust {} is decent, but not a strict improvement.

@iopq

How about renaming them safe? That means "I, the programmer, claim this code is safe, I swear"

You wouldn't call them unsafe blocks, you'd call them blocks declared as safe by the programmer. In other words, explicitly safe blocks as opposed to implicitly safe blocks (everything else).

@sinistersnare

I do not understand any of the arguments for using a positive word, the code in an unsafe block should not be trusted, everything else in the codebase should be trusted. If we have to change it (which we do not) I would again vote for unchecked, even though that is not perfect either.

@iopq

unchecked means "array indeces are not checked to be in bounds, stack is not checked for overflow, etc." which is actually not true in unsafe code, unsafe code is still checked

@sinistersnare

Perfect, unsafe it is.

@nathanaschbacher
@glaebhoerl

The basic issue is that unsafe { ... } means both "this has the potential to be unsafe" and "but you should make sure that it's actually safe". That is the source of all the confusion and bikeshedding. (Which I don't think is very surprising, personally.)

Hence why I think the best way would be to decouple the two: keep unsafe only as a marker of places where unsafe operations may be invoked, and have a separate construct (perhaps at the whole function level, perhaps called trusted) to do the other half, which is to assert that the whole is safe even though its individual parts may not be. (There was an RFC somewhat along these lines, but it was closed.)

@raindev

Wouldn't trusted be redundant that way? As I understand, all the functions are considered safe (having only safe checked code) until unsafe is involved.

@Jurily

flammable?

@iopq

inflammable?

@glaebhoerl

@raindev Normal functions don't need to be trusted - the type system guarantees their safety. Only functions which may violate safety need a level of explicit trust. You're right that a trusted fn would be outwardly semantically indistinguishable from a normal fn: this is more about having the programmer clearly express her intent (an fn containing use of unsafe may be either an unsafe fn or a trusted fn - for an fn without unsafe blocks, there is no choice to make). But perhaps trusted could show up in the generated documentation, and perhaps it could also have ramifications for lints and tooling.

@sinistersnare

if unsafe -> trusted/some_positive_word, the what would regular code be? untrusted?

@nathanaschbacher
@tshepang

@nathanaschbacher great comment

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jun 12, 2014
  1. @steveklabnik
This page is out of date. Refresh to see the latest.
Showing with 50 additions and 0 deletions.
  1. +50 −0 active/0000-replace-unsafe.md
View
50 active/0000-replace-unsafe.md
@@ -0,0 +1,50 @@
+- Start Date: 2014-06-12
+- RFC PR #: (leave this empty)
+- Rust Issue #: (leave this empty)
+
+# Summary
+
+`unsafe` blocks are one of Rust's most important features. However, the name
+"unsafe" doesn't properly communicate the intention of unsafe blocks. I propose
+we change the "unsafe" keyword to "trusted," initially deprecating it.
+
+# Motivation
+
+When explaining Rust to someone who doesn't already know it, there's often
+confusion around `unsafe`. To most of the readers I've spoken with, `unsafe`
+means "This code is not safe." This understanding is incomplete, however.
+`unsafe` actually means "This code is not able to be determined to be safe by
+the compiler, but I promise you that it is." This is a significant difference.
+As indicated in
+[numerous](https://twitter.com/Zalambar/status/477198693783724032)
+[conversations](https://news.ycombinator.com/item?id=7885502), this causes
+confusion.
+
+Furthermore, `unsafe` only [unrestricts certain
+behaviors](http://static.rust-lang.org/doc/0.10/rust.html#behavior-considered-unsafe).
+`unsafe` Rust code is still significantly safer than C. `unsafe` implies that
+_no_ safety checks occur. This is incorrect.
+
+# Detailed design
+
+Replace the "unsafe" keyword with a new keyword, "trusted." For ease of
+transition, "unsafe" should be deprecated, and throw a warning on use. "unsafe"
+can then be removed before 1.0.
+
+# Drawbacks
+
+This would basically invalidate all current code which uses the `unsafe`
+keyword. There's quite a bit of that code. Considering the fix is a simple find
+and replace, I don't believe this drawback is important enough to not change
+the keyword. In addition, a simple deprecation notice means that the older code
+wouldn't be strictly invalid, just throw additional warnings.
+
+# Alternatives
+
+- trust
+- unchecked
+- wrap_unsafe
+
+# Unresolved questions
+
+I am not 100% sure that "trusted" is the best possible name.
Something went wrong with that request. Please try again.