-
Notifications
You must be signed in to change notification settings - Fork 13.4k
Guide macros and unsafe #16331
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Guide macros and unsafe #16331
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -535,7 +535,7 @@ arguments we pass to functions and macros, if you're passing more than one. | |
When you just use the curly braces, Rust will attempt to display the | ||
value in a meaningful way by checking out its type. If you want to specify the | ||
format in a more detailed manner, there are a [wide number of options | ||
available](/std/fmt/index.html). For now, we'll just stick to the default: | ||
available](std/fmt/index.html). For now, we'll just stick to the default: | ||
integers aren't very complicated to print. | ||
|
||
So, we've cleared up all of the confusion around bindings, with one exception: | ||
|
@@ -3520,15 +3520,15 @@ out. | |
In systems programming, pointers are an incredibly important topic. Rust has a | ||
very rich set of pointers, and they operate differently than in many other | ||
languages. They are important enough that we have a specific [Pointer | ||
Guide](/guide-pointers.html) that goes into pointers in much detail. In fact, | ||
Guide](guide-pointers.html) that goes into pointers in much detail. In fact, | ||
while you're currently reading this guide, which covers the language in broad | ||
overview, there are a number of other guides that put a specific topic under a | ||
microscope. You can find the list of guides on the [documentation index | ||
page](/index.html#guides). | ||
page](index.html#guides). | ||
|
||
In this section, we'll assume that you're familiar with pointers as a general | ||
concept. If you aren't, please read the [introduction to | ||
pointers](/guide-pointers.html#an-introduction) section of the Pointer Guide, | ||
pointers](guide-pointers.html#an-introduction) section of the Pointer Guide, | ||
and then come back here. We'll wait. | ||
|
||
Got the gist? Great. Let's talk about pointers in Rust. | ||
|
@@ -4711,4 +4711,177 @@ fail. | |
|
||
# Macros | ||
|
||
One of Rust's most advanced features is its system of **macro**s. While | ||
functions allow you to provide abstractions over values and operations, macros | ||
allow you to provide abstractions over syntax. Do you wish Rust had the ability | ||
to do something that it can't currently do? You may be able to write a macro | ||
to extend Rust's capabilities. | ||
|
||
You've already used one macro extensively: `println!`. When we invoke | ||
a Rust macro, we need to use the exclamation mark (`!`). There's two reasons | ||
that this is true: the first is that it makes it clear when you're using a | ||
macro. The second is that macros allow for flexible syntax, and so Rust must | ||
be able to tell where a macro starts and ends. The `!(...)` helps with this. | ||
|
||
Let's talk some more about `println!`. We could have implemented `println!` as | ||
a function, but it would be worse. Why? Well, what macros allow you to do | ||
is write code that generates more code. So when we call `println!` like this: | ||
|
||
```{rust} | ||
let x = 5i; | ||
println!("x is: {}", x); | ||
``` | ||
|
||
The `println!` macro does a few things: | ||
|
||
1. It parses the string to find any `{}`s | ||
2. It checks that the number of `{}`s matches the number of other arguments. | ||
3. It generates a bunch of Rust code, taking this in mind. | ||
|
||
What this means is that you get type checking at compile time, because | ||
Rust will generate code that takes all of the types into account. If | ||
`println!` was a function, it could still do this type checking, but it | ||
would happen at run time rather than compile time. | ||
|
||
We can check this out using a special flag to `rustc`. This code, in a file | ||
`print.rs`: | ||
|
||
```{rust} | ||
fn main() { | ||
let x = "Hello"; | ||
println!("x is: {:s}", x); | ||
} | ||
``` | ||
|
||
Can have its macros expanded like this: `rustc print.rs --pretty=expanded`, will | ||
give us this huge result: | ||
|
||
```{rust,ignore} | ||
#![feature(phase)] | ||
#![no_std] | ||
#![feature(globs)] | ||
#[phase(plugin, link)] | ||
extern crate std = "std"; | ||
extern crate rt = "native"; | ||
use std::prelude::*; | ||
fn main() { | ||
let x = "Hello"; | ||
match (&x,) { | ||
(__arg0,) => { | ||
#[inline] | ||
#[allow(dead_code)] | ||
static __STATIC_FMTSTR: [::std::fmt::rt::Piece<'static>, ..2u] = | ||
[::std::fmt::rt::String("x is: "), | ||
::std::fmt::rt::Argument(::std::fmt::rt::Argument{position: | ||
::std::fmt::rt::ArgumentNext, | ||
format: | ||
::std::fmt::rt::FormatSpec{fill: | ||
' ', | ||
align: | ||
::std::fmt::rt::AlignUnknown, | ||
flags: | ||
0u, | ||
precision: | ||
::std::fmt::rt::CountImplied, | ||
width: | ||
::std::fmt::rt::CountImplied,},})]; | ||
let __args_vec = | ||
&[::std::fmt::argument(::std::fmt::secret_string, __arg0)]; | ||
let __args = | ||
unsafe { | ||
::std::fmt::Arguments::new(__STATIC_FMTSTR, __args_vec) | ||
}; | ||
::std::io::stdio::println_args(&__args) | ||
} | ||
}; | ||
} | ||
``` | ||
|
||
Intense. Here's a trimmed down version that's a bit easier to read: | ||
|
||
```{rust,ignore} | ||
fn main() { | ||
let x = 5i; | ||
match (&x,) { | ||
(__arg0,) => { | ||
static __STATIC_FMTSTR: = | ||
[String("x is: "), | ||
Argument(Argument { | ||
position: ArgumentNext, | ||
format: FormatSpec { | ||
fill: ' ', | ||
align: AlignUnknown, | ||
flags: 0u, | ||
precision: CountImplied, | ||
width: CountImplied, | ||
}, | ||
}, | ||
]; | ||
let __args_vec = &[argument(secret_string, __arg0)]; | ||
let __args = unsafe { Arguments::new(__STATIC_FMTSTR, __args_vec) }; | ||
|
||
println_args(&__args) | ||
} | ||
}; | ||
} | ||
``` | ||
|
||
Whew! This isn't too terrible. You can see that we still `let x = 5i`, | ||
but then things get a little bit hairy. Three more bindings get set: a | ||
static format string, an argument vector, and the aruments. We then | ||
invoke the `println_args` function with the generated arguments. | ||
|
||
This is the code (well, the full version) that Rust actually compiles. You can | ||
see all of the extra information that's here. We get all of the type safety and | ||
options that it provides, but at compile time, and without needing to type all | ||
of this out. This is how macros are powerful. Without them, you would need to | ||
type all of this by hand to get a type checked `println`. | ||
|
||
For more on macros, please consult [the Macros Guide](guide-macros.html). | ||
Macros are a very advanced and still slightly experimental feature, but don't | ||
require a deep understanding to call, since they look just like functions. The | ||
Guide can help you if you want to write your own. | ||
|
||
# Unsafe | ||
|
||
Finally, there's one more concept that you should be aware in Rust: `unsafe`. | ||
There are two circumstances where Rust's safety provisions don't work well. | ||
The first is when interfacing with C code, and the second is when building | ||
certain kinds of abstractions. | ||
|
||
Rust has support for FFI, (which you can read about in the [FFI | ||
Guide](guide-ffi.html)) but Rust can't guarantee that the C code will be safe, | ||
like Rust's will. Therefore, Rust marks such functions with the `unsafe` | ||
keyword, which indicates that the function may not behave properly. | ||
|
||
Second, if you'd like to create some sort of shared-memory data structure, Rust | ||
won't allow it, because memory must be owned by a single owner. However, if | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This isn't really the case. It's more things like "destructors should run exactly once" and "you need special types to mutate behind a I'm not sure if there's a pithy way to rephrase this though. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think to give a rough understanding, the consequence is enough. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The whole paragraph gives the impression that I don't really have good suggestions, though. I think the way to go could be to enumerate the basic operations that are considered unsafe (raw pointer arithmetic, dereferencing, asm! etc.) and then how you can use them to build safe abstractions with enough care. I guess it somewhat ties to http://doc.rust-lang.org/guide-pointers.html#raw-pointers. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't want to give that much information in the introductory document. That can all go in the unsafe guide. |
||
you're planning on making access to that shared memory safe, such as with a | ||
mutex, _you_ know that it's safe, but Rust can't know. Writing an `unsafe` | ||
block allows you to ask the compiler to trust you. In this case, the _internal_ | ||
implementation of the mutex is considered unsafe, but the _external_ interface | ||
we present is safe. This allows it to be effectively used in normal Rust, while | ||
being able to implement functionality that the compiler can't double check for | ||
us. | ||
|
||
Doesn't an escape hatch undermine the safety of the entire system? Well, if | ||
Rust code segfaults, it _must_ be because of unsafe code somewhere. By | ||
annotating exactly where that is, you have a significantly smaller area to | ||
search. | ||
|
||
We haven't even talked about any examples here, and that's because I want to | ||
emphasize that you should not be writing unsafe code unless you know exactly | ||
what you're doing. The vast majority of Rust developers will only interact with | ||
it when doing FFI, and advanced library authors may use it to build certain | ||
kinds of abstraction. | ||
|
||
# Conclusion | ||
|
||
We covered a lot of ground here. When you've mastered everything in this Guide, | ||
you will have a firm grasp of basic Rust development. There's a whole lot more | ||
out there, we've just covered the surface. There's tons of topics that you can | ||
dig deeper into, and we've built specialized guides for many of them. To learn | ||
more, dig into the [full documentation | ||
index](http://doc.rust-lang.org/index.html). | ||
|
||
Happy hacking! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There isn't function overloading is there? You could never do this but it works with macros right? Seems like a feature.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There isn't currently. I tend not to talk about 'negative features,' but you are right. I think this information belongs in the macros guide though. Remember that these last two sections are supposed to be the briefest of introductions to these topics.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, I meant in your list of items:
4.. Look, it allows overloading! We couldn't do that before; the number of
{}
isn't set!Your call. Just pointing it out.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, it's a good note. I think I'm going to just ignore that detail for now, so I don't have to get into it. Once you say 'rust doesn't have x,' you have to explain why...