From f5ee07792e7d51283b03fa9c241e1aa0517f4637 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Mon, 6 Jan 2014 17:24:16 -0800 Subject: [PATCH 1/4] mk: Fix formatting of docs.mk --- mk/docs.mk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mk/docs.mk b/mk/docs.mk index b27c8f23151..31fa315857e 100644 --- a/mk/docs.mk +++ b/mk/docs.mk @@ -19,8 +19,8 @@ HTML_DEPS := BASE_DOC_OPTS := --from=markdown --standalone --toc --number-sections HTML_OPTS = $(BASE_DOC_OPTS) --to=html5 --section-divs --css=rust.css \ - --include-before-body=doc/version_info.html \ - --include-in-header=doc/favicon.inc + --include-before-body=doc/version_info.html \ + --include-in-header=doc/favicon.inc TEX_OPTS = $(BASE_DOC_OPTS) --to=latex EPUB_OPTS = $(BASE_DOC_OPTS) --to=epub From d4639ade00afe39b41182e0cde7b999ada185f22 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Mon, 6 Jan 2014 17:24:19 -0800 Subject: [PATCH 2/4] doc: Update style of index, organization and trim out some excess. --- doc/index.md | 180 ++++++++++----------------------------------------- 1 file changed, 34 insertions(+), 146 deletions(-) diff --git a/doc/index.md b/doc/index.md index 3e5f2909a77..202814399e9 100644 --- a/doc/index.md +++ b/doc/index.md @@ -1,157 +1,45 @@ % Rust documentation -# Reference docs + + -**Current (0.9)** +[The Rust tutorial](tutorial.html) +[The Rust reference manual](rust.html) ([PDF](rust.pdf)) -* [Tutorial](http://doc.rust-lang.org/doc/0.9/tutorial.html) -* Guides - * [borrowed pointers](http://doc.rust-lang.org/doc/0.9/guide-borrowed-ptr.html) - * [conditions](http://doc.rust-lang.org/doc/0.9/guide-conditions.html) - * [containers & iterators](http://doc.rust-lang.org/doc/0.9/guide-container.html) - * [ffi](http://doc.rust-lang.org/doc/0.9/guide-ffi.html) - * [macros](http://doc.rust-lang.org/doc/0.9/guide-macros.html) - * [rustpkg](http://doc.rust-lang.org/doc/0.9/guide-rustpkg.html) - * [tasks](http://doc.rust-lang.org/doc/0.9/guide-tasks.html) - * [testing](http://doc.rust-lang.org/doc/0.9/guide-testing.html) -* [Manual](http://doc.rust-lang.org/doc/0.9/rust.html) ([PDF](http://doc.rust-lang.org/doc/0.9/rust.pdf)) -* [Standard library](http://doc.rust-lang.org/doc/0.9/std/index.html) -* [Extra library](http://doc.rust-lang.org/doc/0.9/extra/index.html) -* [Package manager](http://doc.rust-lang.org/doc/0.9/rustpkg.html) +# Guides -**In-development (git master)** +[Borrowed Pointers](guide-borrowed-ptr.html) +[Lifetimes](guide-lifetimes.html) +[Containers and Iterators](guide-container.html) +[Tasks and Communication](guide-tasks.html) +[Foreign Function Interface](guide-ffi.html) +[Macros](guide-macros.html) +[Packaging](guide-rustpkg.html) +[Testing](guide-testing.html) +[Conditions](guide-conditions.html) -* [Tutorial](http://doc.rust-lang.org/doc/master/tutorial.html) ([PDF](http://doc.rust-lang.org/doc/master/tutorial.pdf)) -* Guides - * [borrowed pointers](http://doc.rust-lang.org/doc/master/guide-borrowed-ptr.html) - * [conditions](http://doc.rust-lang.org/doc/master/guide-conditions.html) - * [containers & iterators](http://doc.rust-lang.org/doc/master/guide-container.html) - * [ffi](http://doc.rust-lang.org/doc/master/guide-ffi.html) - * [macros](http://doc.rust-lang.org/doc/master/guide-macros.html) - * [rustpkg](http://doc.rust-lang.org/doc/master/guide-rustpkg.html) - * [tasks](http://doc.rust-lang.org/doc/master/guide-tasks.html) - * [testing](http://doc.rust-lang.org/doc/master/guide-testing.html) -* [Manual](http://doc.rust-lang.org/doc/master/rust.html) ([PDF](http://doc.rust-lang.org/doc/master/rust.pdf)) -* [Standard library](http://doc.rust-lang.org/doc/master/std/index.html) -* [Extra library](http://doc.rust-lang.org/doc/master/extra/index.html) -* [libgreen](http://static.rust-lang.org/doc/master/green/index.html) -* [libnative](http://static.rust-lang.org/doc/master/native/index.html) -* [Package manager](http://doc.rust-lang.org/doc/master/rustpkg.html) +# Libraries + +[std](std/index.html) +[extra](index.html) + +# Tooling + +[The rustpkg manual](rustpkg.html) # FAQs -* [Language FAQ][lang-faq] -* [Project FAQ][project-faq] -* [Usage FAQ][usage-faq] -* [Code cheatsheet][cheatsheet] - "How do I do X?" -* [HOWTO submit a bug report][bugreport] +[Language FAQ](complement-lang-faq.html) +[Project FAQ](complement-project-faq.html) +[Usage FAQ](complement-usage-faq.html) +[Code cheatsheet](complement-cheatsheet.html) - "How do I do X?" +[HOWTO submit a bug report](complement-bugreport.html) -[lang-faq]: complement-lang-faq.html -[project-faq]: complement-project-faq.html -[usage-faq]: complement-usage-faq.html -[cheatsheet]: complement-cheatsheet.html -[bugreport]: complement-bugreport.html +# External resources -# Community - - - > **Note** that to guard against botnet attacks we occasionally turn on moderation, disallowing - > unregistered users from joining or talking. You may need to [register](https://wiki.mozilla.org/IRC#Register_your_nickname) your nickname. Sorry for the inconvenience.* - -* IRC - * [#rust on irc.mozilla.org][pound-rust] - Main Rust channel - general discussion - * [#rust-internals on irc.mozilla.org][pound-rust-internals] - Rust compiler and library development - * [#rust-gamedev on irc.mozilla.org][pound-rust-gamedev] - game development in Rust - * [#rust-osdev on irc.mozill.org][pound-rust-osdev] - OS development in Rust - * [#rust on irc.ozinger.org][pound-rust-korea] - Korean Rust community - -* Mailing list [rust-dev] -* Reddit's [r/rust] -* User groups - * [Rust Bay Area][rust-bay-area] - * [Rust Korea][rust-korea] - * [Rust Skåne][rust-skane] - * [Rust 中文圈][rust-zh] (on Google+) - -[pound-rust]: http://chat.mibbit.com/?server=irc.mozilla.org&channel=%23rust -[pound-rust-internals]: http://chat.mibbit.com/?server=irc.mozilla.org&channel=%23rust-internals -[pound-rust-gamedev]: http://chat.mibbit.com/?server=irc.mozilla.org&channel=%23rust-gamedev -[pound-rust-osdev]: http://chat.mibbit.com/?server=irc.mozilla.org&channel=%23rust-osdev -[pound-rust-korea]: http://chat.mibbit.com/?server=irc.ozinger.org&channel=%23rust -[rust-dev]: https://mail.mozilla.org/listinfo/rust-dev -[r/rust]: http://reddit.com/r/rust -[rust-bay-area]: http://www.meetup.com/Rust-Bay-Area/ -[rust-korea]: http://rust-kr.org/ -[rust-skane]: http://www.meetup.com/rust-skane/ -[rust-zh]: https://plus.google.com/communities/100629002107624231185/ - -# Specialized documentation - -[Releases][release-notes] - Links to current and old releases and documentation -[Detailed release notes][detailed-release-notes] - Further explanation of language changes -[Rust for C++ programmers][rust-for-c] - A cheat sheet -[Rusticon][rust-icon] - A glossary of terms commonly used in Rust and Rust tools. -[Unit testing][unit-testing] - Writing tests and running them with the built-in test driver -[Using rustpkg][rustpkg] - Managing packages -[Using rustdoc][rustdoc] - How to extract Markdown and HTML documentation from code -[Package documentation](http://docs.octayn.net/) - Documentation for rust packages -[Continuous integration][ci] - Test your GitHub-hosted packages with Travis CI -[Reading and writing files][doc-rw] -[Attributes][doc-attributes] - The role of metadata in Rust code, with descriptions of many applications -[Packages, editors, and other tools][tools] -[Packaging Terminology][doc-terminology] -[Crate Hashes][crate-hashes] - How Rust generates crate filenames, versions symbols, and why -[Computer Graphics and Game Development][game-dev] - Libraries and example projects -[Présentation du langage Rust](http://lea-linux.org/documentations/Rust) - Detailed documentation in French, with examples -[Building for Android][building-android] -[Building for iOS][building-ios] - -[release-notes]: https://github.com/mozilla/rust/wiki/Doc-releases -[detailed-release-notes]: https://github.com/mozilla/rust/wiki/Doc-detailed-release-notes -[rust-for-c]: https://github.com/mozilla/rust/wiki/Rust-for-CXX-programmers -[rust-icon]: https://github.com/mozilla/rust/wiki/The-Rusticon -[unit-testing]: https://github.com/mozilla/rust/wiki/Doc-unit-testing -[rustpkg]: https://github.com/mozilla/rust/wiki/Rustpkg -[rustdoc]: https://github.com/mozilla/rust/wiki/Doc-using-rustdoc -[ci]: https://github.com/mozilla/rust/wiki/Doc-continuous-integration -[doc-rw]: https://github.com/mozilla/rust/wiki/Doc-reading-and-writing-files -[doc-attributes]: https://github.com/mozilla/rust/wiki/Doc-attributes -[tools]: https://github.com/mozilla/rust/wiki/Doc-packages%2C-editors%2C-and-other-tools -[doc-terminology]: https://github.com/mozilla/rust/wiki/Doc-packaging-terminology -[crate-hashes]: https://github.com/mozilla/rust/wiki/Doc-crate-hashes -[game-dev]: https://github.com/mozilla/rust/wiki/Computer-Graphics-and-Game-Development -[building-android]: https://github.com/mozilla/rust/wiki/Doc-building-for-android -[building-ios]: https://github.com/mozilla/rust/wiki/Doc-building-for-ios - -Some Rust classics: - -* [Pointers in Rust: A Guide](http://words.steveklabnik.com/pointers-in-rust-a-guide) -* [A taste of Rust](https://lwn.net/Articles/547145/) -* [An overview of memory management in Rust](http://pcwalton.github.com/blog/2013/03/18/an-overview-of-memory-management-in-rust/) -* [Which pointer should I use?](http://pcwalton.github.com/blog/2013/03/09/which-pointer-should-i-use/) -* [Lifetimes explained](http://maikklein.github.io/2013/08/27/lifetimes-explained/) -* [Little things that matter in language design](http://lwn.net/Articles/553131/) -* [Operator overloading in Rust](http://maniagnosis.crsr.net/2013/04/operator-overloading-in-rust.html) -* [Embedding Rust in Ruby](http://brson.github.com/2013/03/10/embedding-rust-in-ruby/) -* [A first parallel program in Rust](http://blog.leahhanson.us/a-first-parallel-program-in-rust.html) -* [FizzBuzz revisited](http://composition.al/blog/2013/03/02/fizzbuzz-revisited/) -* [Ownership types in Rust, and whether they're worth it](http://tim.dreamwidth.org/1784423.html) -* [Reasoning about the heap in Rust](http://johnbender.us/2013/04/30/reasoning-about-the-heap-in-rust) -* [The Option Type](http://nickdesaulniers.github.io/blog/2013/05/07/rust-pattern-matching-and-the-option-type/) -* [How I got started hacking rustc](http://cmr.github.io/blog/2013/06/23/how-i-got-started-with-rust/) -* [Abstraction penalties, stack allocation, and ownership types](http://robert.ocallahan.org/2007/10/abstraction-penalties-stack-allocation_23.html) -* [Présentation de Rust 0.8](http://linuxfr.org/news/presentation-de-rust-0-8) - A very detailed article about Rust 0.8, in French! - -# Presentations - -* [John Clements, 10-minute talk (video)](http://www.youtube.com/watch?v=_KgXy7jnwhY) at SoCal PLS on Rust, Macros, and Hygiene. December 2013. -* [Felix's Codemesh 2013 slides](http://pnkfelix.github.io/present-rust-codemesh2013/fklock-rust-codemesh2013.pdf) -* Geoffroy Couprie's [Scala.IO 2013 slides](http://dev.unhandledexpression.com/slides/rust-scalaio/) -* Steve's presentation at RuPy 2013 "Nobody Knows Rust." [slides](http://steveklabnik.github.io/nobody_knows_rust/#/), video to come soon -* [Tim's presentation at OS Bridge 2013](http://opensourcebridge.org/sessions/970) - And [slides](http://opensourcebridge.org/wiki/2013/Rust%3A_A_Friendly_Introduction) -* [Niko's presentation at Northeastern](http://smallcultfollowing.com/babysteps/blog/2013/07/18/rust-presentation-at-northeastern/) - Slides only -* [An I/O system for Rust](https://air.mozilla.org/intern-presentations-reed/) - Eric Reed's intern presentation on I/O -* [Types of Types](https://air.mozilla.org/ben-blum-from-the-research-team-presents-types-of-types-in-rust/) - Ben Blum's intern presentation on 'kinds' -* [Default methods in Rust](https://air.mozilla.org/intern-presentation-sullivan/) - Michael Sullivan's intern presentation on default methods -* [A work stealing runtime for Rust](https://air.mozilla.org/2013-intern-todd/) - Aaron Todd's intern presentation on the Rust scheduler -* [Dave Herman's StrangeLoop 2012 talk](http://www.infoq.com/presentations/Rust) +The Rust [IRC channel](http://chat.mibbit.com/?server=irc.mozilla.org&channel=%23rust) - #rust on irc.mozilla.org +The Rust community on [Reddit](http://reddit.com/r/rust) +The Rust [wiki](http://github.com/mozilla/rust/wiki) From 6f09d80f97457fd69fa736d71c491042b2c945df Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Fri, 27 Sep 2013 17:03:34 -0700 Subject: [PATCH 3/4] Add Pointer tutorial, rename borrowed pointer tutorial. --- configure | 3 +- ...ide-borrowed-ptr.md => guide-lifetimes.md} | 0 doc/guide-pointers.md | 475 ++++++++++++++++++ doc/index.md | 2 +- doc/tutorial.md | 15 +- mk/docs.mk | 10 +- mk/tests.mk | 5 +- 7 files changed, 497 insertions(+), 13 deletions(-) rename doc/{guide-borrowed-ptr.md => guide-lifetimes.md} (100%) create mode 100644 doc/guide-pointers.md diff --git a/configure b/configure index dbbd1a2bb5f..b40d303fe7f 100755 --- a/configure +++ b/configure @@ -800,7 +800,8 @@ do make_dir $h/test/doc-tutorial make_dir $h/test/doc-guide-ffi make_dir $h/test/doc-guide-macros - make_dir $h/test/doc-guide-borrowed-ptr + make_dir $h/test/doc-guide-lifetimes + make_dir $h/test/doc-guide-pointers make_dir $h/test/doc-guide-container make_dir $h/test/doc-guide-tasks make_dir $h/test/doc-guide-conditions diff --git a/doc/guide-borrowed-ptr.md b/doc/guide-lifetimes.md similarity index 100% rename from doc/guide-borrowed-ptr.md rename to doc/guide-lifetimes.md diff --git a/doc/guide-pointers.md b/doc/guide-pointers.md new file mode 100644 index 00000000000..4028ed203cd --- /dev/null +++ b/doc/guide-pointers.md @@ -0,0 +1,475 @@ +% The Rust Pointer Guide + +Rust's pointers are one of its more unique and compelling features. Pointers +are also one of the more confusing topics for newcomers to Rust. They can also +be confusing for people coming from other languages that support pointers, such +as C++. This tutorial will help you understand this important topic. + +# You don't actually need pointers + +I have good news for you: you probably don't need to care about pointers, +especially as you're getting started. Think of it this way: Rust is a language +that emphasizes safety. Pointers, as the joke goes, are very pointy: it's easy +to accidentally stab yourself. Therefore, Rust is made in a way such that you +don't need them very often. + +"But tutorial!" you may cry. "My co-worker wrote a function that looks like +this: + +```rust +fn succ(x: &int) -> int { *x + 1 } +``` + +So I wrote this code to try it out: + +```rust +fn main() { + let number = 5; + let succ_number = succ(number); + println!("{}", succ_number); +} +``` + +And now I get an error: + +``` +error: mismatched types: expected `&int` but found `` (expected &-ptr but found integral variable) +``` + +What gives? It needs a pointer! Therefore I have to use pointers!" + +Turns out, you don't. All you need is a reference. Try this on for size: + +```rust +fn main() { + let number = 5; + let succ_number = succ(&number); + println!("{}", succ_number); +} +``` + +It's that easy! One extra little `&` there. This code will run, and print `6`. + +That's all you need to know. Your co-worker could have written the function +like this: + +```rust +fn succ(x: int) -> int { x + 1 } + +fn main() { + let number = 5; + let succ_number = succ(number); + println!("{}", succ_number); +} +``` + +No pointers even needed. Then again, this is a simple example. I assume that +your real-world `succ` function is more complicated, and maybe your co-worker +had a good reason for `x` to be a pointer of some kind. In that case, references +are your best friend. Don't worry about it, life is too short. + +However. + +Here are the use-cases for pointers. I've prefixed them with the name of the +pointer that satisfies that use-case: + +1. Owned: ~Trait must be a pointer, becuase you don't know the size of the +object, so indirection is mandatory. +2. Owned: You need a recursive data structure. These can be infinite sized, so +indirection is mandatory. +3. Owned: A very, very, very rare situation in which you have a *huge* chunk of +data that you wish to pass to many methods. Passing a pointer will make this +more efficient. If you're coming from another language where this technique is +common, such as C++, please read "A note..." below. +4. Managed: Having only a single owner to a piece of data would be inconvenient +or impossible. This is only often useful when a program is very large or very +complicated. Using a managed pointer will activate Rust's garbage collection +mechanism. +5: Borrowed: You're writing a function, and you need a pointer, but you don't +care about its ownership. If you make the argument a borrowed pointer, callers +can send in whatever kind they want. + +Five exceptions. That's it. Otherwise, you shouldn't need them. Be skeptical +of pointers in Rust: use them for a deliberate purpose, not just to make the +compiler happy. + +## A note for those proficient in pointers + +If you're coming to Rust from a language like C or C++, you may be used to +passing things by reference, or passing things by pointer. In some langauges, +like Java, you can't even have objects without a pointer to them. Therefore, if +you were writing this Rust code: + +```rust +struct Point { + x: int, + y: int, +} + +fn main() { + let p0 = Point { x: 5, y: 10}; + let p1 = transform(p0); + println!("{:?}", p1); +} + +``` + +I think you'd implement `transform` like this: + +```rust +fn transform(p: &Point) -> Point { + Point { x: p.x + 1, y: p.y + 1} +} + +// and change this: +let p1 = transform(&p0); +``` + +This does work, but you don't need to create those references! The better way to write this is simply: + +```rust +struct Point { + x: int, + y: int, +} + +fn transform(p: Point) -> Point { + Point { x: p.x + 1, y: p.y + 1} +} + +fn main() { + let p0 = Point { x: 5, y: 10}; + let p1 = transform(p0); + println!("{:?}", p1); +} +``` + +But won't this be inefficent? Well, that's a complicated question, but it's +important to know that Rust, like C and C++, store aggregate data types +'unboxed,' whereas languages like Java and Ruby store these types as 'boxed.' +For smaller structs, this way will be more efficient. For larger ones, it may +be less so. But don't reach for that pointer until you must! Make sure that the +struct is large enough by performing some tests before you add in the +complexity of pointers. + +# Owned Pointers + +Owned pointers are the conceptually simplest kind of pointer in Rust. A rough +approximation of owned pointers follows: + +1. Only one owned pointer may exist to a particular place in memory. It may be +borrowed from that owner, however. +2. The Rust compiler uses static analysis to determine where the pointer is in +scope, and handles allocating and de-allocating that memory. Owned pointers are +not garbage collected. + +These two properties make for three use cases. + +## References to Traits + +Traits must be referenced through a pointer, becuase the struct that implements +the trait may be a different size than a different struct that implements the +trait. Therefore, unboxed traits don't make any sense, and aren't allowed. + +## Recursive Data Structures + +Sometimes, you need a recursive data structure. The simplest is known as a 'cons list': + +```rust +enum List { + Nil, + Cons(T, ~List), +} + +fn main() { + let list: List = Cons(1, ~Cons(2, ~Cons(3, ~Nil))); + println!("{:?}", list); +} +``` + +This prints: + +``` +Cons(1, ~Cons(2, ~Cons(3, ~Nil))) +``` + +The inner lists _must_ be an owned pointer, becuase we can't know how many +elements are in the list. Without knowing the length, we don't know the size, +and therefore require the indirection that pointers offer. + +## Efficiency + +This should almost never be a concern, but because creating an owned pointer +boxes its value, it therefore makes referring to the value the size of the box. +This may make passing an owned pointer to a function less expensive than +passing the value itself. Don't worry yourself with this case until you've +proved that it's an issue through benchmarks. + +For example, this will work: + +```rust +struct Point { + x: int, + y: int, +} + +fn main() { + let a = Point { x: 10, y: 20 }; + do spawn { + println(a.x.to_str()); + } +} +``` + +This struct is tiny, so it's fine. If `Point` were large, this would be more +efficient: + +```rust +struct Point { + x: int, + y: int, +} + +fn main() { + let a = ~Point { x: 10, y: 20 }; + do spawn { + println(a.x.to_str()); + } +} +``` + +Now it'll be copying a pointer-sized chunk of memory rather than the whole +struct. + +# Managed Pointers + +Managed pointers, notated by an `@`, are used when having a single owner for +some data isn't convenient or possible. This generally happens when your +program is very large and complicated. + +For example, let's say you're using an owned pointer, and you want to do this: + +```rust +struct Point { + x: int, + y: int, +} + +fn main() { + let a = ~Point { x: 10, y: 20 }; + let b = a; + println(b.x.to_str()); + println(a.x.to_str()); +} +``` + +You'll get this error: + +``` +test.rs:10:12: 10:13 error: use of moved value: `a` +test.rs:10 println(a.x.to_str()); + ^ +test.rs:8:8: 8:9 note: `a` moved here because it has type `~Point`, which is moved by default (use `ref` to override) +test.rs:8 let b = a; + ^ +``` + +As the message says, owned pointers only allow for one owner at a time. When you assign `a` to `b`, `a` becomes invalid. Change your code to this, however: + +```rust +struct Point { + x: int, + y: int, +} + +fn main() { + let a = @Point { x: 10, y: 20 }; + let b = a; + println(b.x.to_str()); + println(a.x.to_str()); +} +``` + +And it works: + +``` +10 +10 +``` + +So why not just use managed pointers everywhere? There are two big drawbacks to +managed pointers: + +1. They activate Rust's garbage collector. Other pointer types don't share this +drawback. +2. You cannot pass this data to another task. Shared ownership across +concurrency boundaries is the source of endless pain in other langauges, so +Rust does not let you do this. + +# Borrowed Pointers + +Borrowed pointers are the third major kind of pointer Rust supports. They are +simultaneously the simplest and the most complicated kind. Let me explain: +they're called 'borrowed' pointers because they claim no ownership over the +data they're pointing to. They're just borrowing it for a while. So in that +sense, they're simple: just keep whatever ownership the data already has. For +example: + +```rust +use std::num::sqrt; + +struct Point { + x: float, + y: float, +} + +fn compute_distance(p1: &Point, p2: &Point) -> float { + let x_d = p1.x - p2.x; + let y_d = p1.y - p2.y; + + sqrt(x_d * x_d + y_d * y_d) +} + +fn main() { + let origin = @Point { x: 0.0, y: 0.0 }; + let p1 = ~Point { x: 5.0, y: 3.0 }; + + println!("{:?}", compute_distance(origin, p1)); +} +``` + +This prints `5.83095189`. You can see that the `compute_distance` function +takes in two borrowed pointers, but we give it a managed and unique pointer. Of +course, if this were a real program, we wouldn't have any of these pointers, +they're just there to demonstrate the concepts. + +So how is this hard? Well, because we're igorning ownership, the compiler needs +to take great care to make sure that everything is safe. Despite their complete +safety, a borrowed pointer's representation at runtime is the same as that of +an ordinary pointer in a C program. They introduce zero overhead. The compiler +does all safety checks at compile time. + +This theory is called 'region pointers,' and involve a concept called +'lifetimes'. Here's the simple explanation: would you expect this code to +compile? + +```rust +fn main() { + println(x.to_str()); + let x = 5; +} +``` + +Probably not. That's becuase you know that the name `x` is valid from where +it's declared to when it goes out of scope. In this case, that's the end of +the `main` function. So you know this code will cause an error. We call this +duration a 'lifetime'. Let's try a more complex example: + +```rust +fn main() { + let mut x = ~5; + if(*x < 10) { + let y = &x; + println!("Oh no: {:?}", y); + return; + } + *x = *x - 1; + println!("Oh no: {:?}", x); +} +``` + +Here, we're borrowing a pointer to `x` inside of the `if`. The compiler, however, +is able to determine that that pointer will go out of scope without `x` being +mutated, and therefore, lets us pass. This wouldn't work: + +```rust +fn main() { + let mut x = ~5; + if(*x < 10) { + let y = &x; + *x = *x - 1; + + println!("Oh no: {:?}", y); + return; + } + *x = *x - 1; + println!("Oh no: {:?}", x); +} +``` + +It gives this error: + +``` +test.rs:5:8: 5:10 error: cannot assign to `*x` because it is borrowed +test.rs:5 *x = *x - 1; + ^~ +test.rs:4:16: 4:18 note: borrow of `*x` occurs here +test.rs:4 let y = &x; + ^~ +``` + +As you might guess, this kind of analysis is complex for a human, and therefore +hard for a computer, too! There is an entire [tutorial devoted to borrowed +pointers and lifetimes](tutorial-lifetimes.html) that goes into lifetimes in +great detail, so if you want the full details, check that out. + +# Returning Pointers + +We've talked a lot about funtions that accept various kinds of pointers, but +what about returning them? Here's the rule of thumb: only return a unique or +managed pointer if you were given one in the first place. + +What does that mean? Don't do this: + +```rust +fn foo(x: ~int) -> ~int { + return ~*x; +} + +fn main() { + let x = ~5; + let y = foo(x); +} +``` + +Do this: + +```rust +fn foo(x: ~int) -> int { + return *x; +} + +fn main() { + let x = ~5; + let y = ~foo(x); +} +``` + +This gives you flexibility, without sacrificing performance. For example, this will +also work: + +```rust +fn foo(x: ~int) -> int { + return *x; +} + +fn main() { + let x = ~5; + let y = @foo(x); +} +``` + +You may think that this gives us terrible performance: return a value and then +immediately box it up?!?! Isn't that the worst of both worlds? Rust is smarter +than that. There is no copy in this code. `main` allocates enough room for the +`@int`, passes it into `foo` as `x`, and then `foo` writes the value into the +new box. This writes the return value directly into the allocated box. + +This is important enough that it bears repeating: pointers are not for optimizing +returning values from your code. Allow the caller to choose how they want to +use your output. + + +# Related Resources + +* [Lifetimes tutorial](tutorial-lifetimes.html) diff --git a/doc/index.md b/doc/index.md index 202814399e9..f1351c1489c 100644 --- a/doc/index.md +++ b/doc/index.md @@ -11,7 +11,7 @@ # Guides -[Borrowed Pointers](guide-borrowed-ptr.html) +[Pointers](guide-pointers.html) [Lifetimes](guide-lifetimes.html) [Containers and Iterators](guide-container.html) [Tasks and Communication](guide-tasks.html) diff --git a/doc/tutorial.md b/doc/tutorial.md index 2f017c77c0b..18766195ce2 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -1423,10 +1423,8 @@ intuitive sense: you must wait for a borrowed value to be returned (that is, for the borrowed pointer to go out of scope) before you can make full use of it again. -For a more in-depth explanation of borrowed pointers, read the -[borrowed pointer tutorial][borrowtut]. - -[borrowtut]: tutorial-borrowed-ptr.html +For a more in-depth explanation of borrowed pointers and lifetimes, read the +[lifetimes and borrowed pointer tutorial][lifetimes]. ## Freezing @@ -3265,7 +3263,8 @@ re-export a bunch of 'officially blessed' crates that get managed with `rustpkg` Now that you know the essentials, check out any of the additional guides on individual topics. -* [Borrowed pointers][borrow] +* [Pointers][pointers] +* [Lifetimes][lifetimes] * [Tasks and communication][tasks] * [Macros][macros] * [The foreign function interface][ffi] @@ -3275,9 +3274,10 @@ guides on individual topics. * [Documenting Rust code][rustdoc] * [Testing Rust code][testing] -There is further documentation on the [Main Page](index.html). +There is further documentation on the [wiki], however those tend to be even more out of date as this document. -[borrow]: guide-borrowed-ptr.html +[pointers]: guide-pointers.html +[lifetimes]: guide-lifetimes.html [tasks]: guide-tasks.html [macros]: guide-macros.html [ffi]: guide-ffi.html @@ -3286,5 +3286,6 @@ There is further documentation on the [Main Page](index.html). [rustpkg]: guide-rustpkg.html [testing]: guide-testing.html [rustdoc]: rustdoc.html +[wiki]: https://github.com/mozilla/rust/wiki/Docs [wiki-packages]: https://github.com/mozilla/rust/wiki/Doc-packages,-editors,-and-other-tools diff --git a/mk/docs.mk b/mk/docs.mk index 31fa315857e..7859feae5f5 100644 --- a/mk/docs.mk +++ b/mk/docs.mk @@ -194,8 +194,8 @@ doc/guide-testing.html: $(S)doc/guide-testing.md $(HTML_DEPS) $(Q)$(CFG_NODE) $(S)doc/prep.js --highlight $< | \ $(CFG_PANDOC) $(HTML_OPTS) --output=$@ -DOCS += doc/guide-borrowed-ptr.html -doc/guide-borrowed-ptr.html: $(S)doc/guide-borrowed-ptr.md $(HTML_DEPS) +DOCS += doc/guide-lifetimes.html +doc/guide-lifetimes.html: $(S)doc/guide-lifetimes.md $(HTML_DEPS) @$(call E, pandoc: $@) $(Q)$(CFG_NODE) $(S)doc/prep.js --highlight $< | \ $(CFG_PANDOC) $(HTML_OPTS) --output=$@ @@ -218,6 +218,12 @@ doc/guide-rustpkg.html: $(S)doc/guide-rustpkg.md $(HTML_DEPS) $(Q)$(CFG_NODE) $(S)doc/prep.js --highlight $< | \ $(CFG_PANDOC) $(HTML_OPTS) --output=$@ +DOCS += doc/guide-pointers.html +doc/guide-pointers.html: $(S)doc/guide-pointers.md $(HTML_DEPS) + @$(call E, pandoc: $@) + $(Q)$(CFG_NODE) $(S)doc/prep.js --highlight $< | \ + $(CFG_PANDOC) $(HTML_OPTS) --output=$@ + ifeq ($(CFG_PDFLATEX),) $(info cfg: no pdflatex found, omitting doc/rust.pdf) else diff --git a/mk/tests.mk b/mk/tests.mk index 45e45c42d84..8bb6f7be55b 100644 --- a/mk/tests.mk +++ b/mk/tests.mk @@ -20,8 +20,9 @@ TEST_HOST_CRATES = rustpkg rustc rustdoc syntax TEST_CRATES = $(TEST_TARGET_CRATES) $(TEST_HOST_CRATES) # Markdown files under doc/ that should have their code extracted and run -DOC_TEST_NAMES = tutorial guide-ffi guide-macros guide-borrowed-ptr \ - guide-tasks guide-conditions guide-container rust +DOC_TEST_NAMES = tutorial guide-ffi guide-macros guide-lifetimes \ + guide-tasks guide-conditions guide-container guide-pointers \ + rust ###################################################################### # Environment configuration From 280553c98842c62b5addc6c0ae444a0ec21ee530 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Mon, 6 Jan 2014 19:30:26 -0800 Subject: [PATCH 4/4] doc: Fix tests in pointer guide --- doc/guide-pointers.md | 109 ++++++++++++++++++++++-------------------- 1 file changed, 58 insertions(+), 51 deletions(-) diff --git a/doc/guide-pointers.md b/doc/guide-pointers.md index 4028ed203cd..8777eb2e577 100644 --- a/doc/guide-pointers.md +++ b/doc/guide-pointers.md @@ -16,44 +16,45 @@ don't need them very often. "But tutorial!" you may cry. "My co-worker wrote a function that looks like this: -```rust +~~~rust fn succ(x: &int) -> int { *x + 1 } -``` +~~~ So I wrote this code to try it out: -```rust +~~~rust {.xfail-test} fn main() { let number = 5; let succ_number = succ(number); println!("{}", succ_number); } -``` +~~~ And now I get an error: -``` +~~~ {.notrust} error: mismatched types: expected `&int` but found `` (expected &-ptr but found integral variable) -``` +~~~ What gives? It needs a pointer! Therefore I have to use pointers!" Turns out, you don't. All you need is a reference. Try this on for size: -```rust +~~~rust +# fn succ(x: &int) -> int { *x + 1 } fn main() { let number = 5; let succ_number = succ(&number); println!("{}", succ_number); } -``` +~~~ It's that easy! One extra little `&` there. This code will run, and print `6`. That's all you need to know. Your co-worker could have written the function like this: -```rust +~~~rust fn succ(x: int) -> int { x + 1 } fn main() { @@ -61,7 +62,7 @@ fn main() { let succ_number = succ(number); println!("{}", succ_number); } -``` +~~~ No pointers even needed. Then again, this is a simple example. I assume that your real-world `succ` function is more complicated, and maybe your co-worker @@ -100,7 +101,8 @@ passing things by reference, or passing things by pointer. In some langauges, like Java, you can't even have objects without a pointer to them. Therefore, if you were writing this Rust code: -```rust +~~~rust +# fn transform(p: Point) -> Point { p } struct Point { x: int, y: int, @@ -112,22 +114,27 @@ fn main() { println!("{:?}", p1); } -``` +~~~ I think you'd implement `transform` like this: -```rust +~~~rust +# struct Point { +# x: int, +# y: int, +# } +# let p0 = Point { x: 5, y: 10}; fn transform(p: &Point) -> Point { Point { x: p.x + 1, y: p.y + 1} } // and change this: let p1 = transform(&p0); -``` +~~~ This does work, but you don't need to create those references! The better way to write this is simply: -```rust +~~~rust struct Point { x: int, y: int, @@ -142,7 +149,7 @@ fn main() { let p1 = transform(p0); println!("{:?}", p1); } -``` +~~~ But won't this be inefficent? Well, that's a complicated question, but it's important to know that Rust, like C and C++, store aggregate data types @@ -175,7 +182,7 @@ trait. Therefore, unboxed traits don't make any sense, and aren't allowed. Sometimes, you need a recursive data structure. The simplest is known as a 'cons list': -```rust +~~~rust enum List { Nil, Cons(T, ~List), @@ -185,13 +192,13 @@ fn main() { let list: List = Cons(1, ~Cons(2, ~Cons(3, ~Nil))); println!("{:?}", list); } -``` +~~~ This prints: -``` +~~~ {.notrust} Cons(1, ~Cons(2, ~Cons(3, ~Nil))) -``` +~~~ The inner lists _must_ be an owned pointer, becuase we can't know how many elements are in the list. Without knowing the length, we don't know the size, @@ -207,7 +214,7 @@ proved that it's an issue through benchmarks. For example, this will work: -```rust +~~~rust struct Point { x: int, y: int, @@ -219,12 +226,12 @@ fn main() { println(a.x.to_str()); } } -``` +~~~ This struct is tiny, so it's fine. If `Point` were large, this would be more efficient: -```rust +~~~rust struct Point { x: int, y: int, @@ -236,7 +243,7 @@ fn main() { println(a.x.to_str()); } } -``` +~~~ Now it'll be copying a pointer-sized chunk of memory rather than the whole struct. @@ -249,7 +256,7 @@ program is very large and complicated. For example, let's say you're using an owned pointer, and you want to do this: -```rust +~~~rust {.xfail-test} struct Point { x: int, y: int, @@ -261,22 +268,22 @@ fn main() { println(b.x.to_str()); println(a.x.to_str()); } -``` +~~~ You'll get this error: -``` +~~~ {.notrust} test.rs:10:12: 10:13 error: use of moved value: `a` test.rs:10 println(a.x.to_str()); ^ test.rs:8:8: 8:9 note: `a` moved here because it has type `~Point`, which is moved by default (use `ref` to override) test.rs:8 let b = a; ^ -``` +~~~ As the message says, owned pointers only allow for one owner at a time. When you assign `a` to `b`, `a` becomes invalid. Change your code to this, however: -```rust +~~~rust struct Point { x: int, y: int, @@ -288,14 +295,14 @@ fn main() { println(b.x.to_str()); println(a.x.to_str()); } -``` +~~~ And it works: -``` +~~~ {.notrust} 10 10 -``` +~~~ So why not just use managed pointers everywhere? There are two big drawbacks to managed pointers: @@ -315,15 +322,15 @@ data they're pointing to. They're just borrowing it for a while. So in that sense, they're simple: just keep whatever ownership the data already has. For example: -```rust +~~~rust use std::num::sqrt; struct Point { - x: float, - y: float, + x: f32, + y: f32, } -fn compute_distance(p1: &Point, p2: &Point) -> float { +fn compute_distance(p1: &Point, p2: &Point) -> f32 { let x_d = p1.x - p2.x; let y_d = p1.y - p2.y; @@ -336,7 +343,7 @@ fn main() { println!("{:?}", compute_distance(origin, p1)); } -``` +~~~ This prints `5.83095189`. You can see that the `compute_distance` function takes in two borrowed pointers, but we give it a managed and unique pointer. Of @@ -353,19 +360,19 @@ This theory is called 'region pointers,' and involve a concept called 'lifetimes'. Here's the simple explanation: would you expect this code to compile? -```rust +~~~rust {.xfail-test} fn main() { println(x.to_str()); let x = 5; } -``` +~~~ Probably not. That's becuase you know that the name `x` is valid from where it's declared to when it goes out of scope. In this case, that's the end of the `main` function. So you know this code will cause an error. We call this duration a 'lifetime'. Let's try a more complex example: -```rust +~~~rust fn main() { let mut x = ~5; if(*x < 10) { @@ -376,13 +383,13 @@ fn main() { *x = *x - 1; println!("Oh no: {:?}", x); } -``` +~~~ Here, we're borrowing a pointer to `x` inside of the `if`. The compiler, however, is able to determine that that pointer will go out of scope without `x` being mutated, and therefore, lets us pass. This wouldn't work: -```rust +~~~rust {.xfail-test} fn main() { let mut x = ~5; if(*x < 10) { @@ -395,18 +402,18 @@ fn main() { *x = *x - 1; println!("Oh no: {:?}", x); } -``` +~~~ It gives this error: -``` +~~~ {.notrust} test.rs:5:8: 5:10 error: cannot assign to `*x` because it is borrowed test.rs:5 *x = *x - 1; ^~ test.rs:4:16: 4:18 note: borrow of `*x` occurs here test.rs:4 let y = &x; ^~ -``` +~~~ As you might guess, this kind of analysis is complex for a human, and therefore hard for a computer, too! There is an entire [tutorial devoted to borrowed @@ -421,7 +428,7 @@ managed pointer if you were given one in the first place. What does that mean? Don't do this: -```rust +~~~rust fn foo(x: ~int) -> ~int { return ~*x; } @@ -430,11 +437,11 @@ fn main() { let x = ~5; let y = foo(x); } -``` +~~~ Do this: -```rust +~~~rust fn foo(x: ~int) -> int { return *x; } @@ -443,12 +450,12 @@ fn main() { let x = ~5; let y = ~foo(x); } -``` +~~~ This gives you flexibility, without sacrificing performance. For example, this will also work: -```rust +~~~rust fn foo(x: ~int) -> int { return *x; } @@ -457,7 +464,7 @@ fn main() { let x = ~5; let y = @foo(x); } -``` +~~~ You may think that this gives us terrible performance: return a value and then immediately box it up?!?! Isn't that the worst of both worlds? Rust is smarter