Skip to content

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

Merged
merged 2 commits into from
Aug 21, 2014
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
181 changes: 177 additions & 4 deletions src/doc/guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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);
Copy link
Contributor

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.

println("{}", x);
println("{}, {}", x, y);

Copy link
Member Author

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.

Copy link
Contributor

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:

  1. It parses the string to find any {}s
  2. It checks that the number of {}s matches the numer of other arguments.
  3. It generates a bunch of Rust code, taking this in mind.

4.. Look, it allows overloading! We couldn't do that before; the number of {} isn't set!

Your call. Just pointing it out.

Copy link
Member Author

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...

```

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
Copy link
Member

Choose a reason for hiding this comment

The 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 &" and "&mut references shouldn't alias". It is true that Rust's way to enforce this is via memory naturally being uniquely owned, but it's not the fundamental reason (just a consequence).

I'm not sure if there's a pithy way to rephrase this though.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think to give a rough understanding, the consequence is enough.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The whole paragraph gives the impression that unsafe in a non-FFI context is only for when you're dealing with shared memory. But it's also useful for when you have a single owner and want to manipulate a raw pointer, via pointer arithmetic for instance, in a way that the compiler cannot prove sound, which is what some iterator implementations do under the hood.

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.

Copy link
Member Author

Choose a reason for hiding this comment

The 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!