From 6a778ca0a7044b22282e8c7c16fef0bc7e16b1d4 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Thu, 20 Dec 2012 00:30:04 -0800 Subject: [PATCH 01/26] doc: 'trait constraints' -> 'inheritance'. Expand /cc: #4217 --- doc/rust.md | 29 ++++++++++++++++++++++++++++- doc/tutorial.md | 34 +++++++++++++++++++++++++++++++--- 2 files changed, 59 insertions(+), 4 deletions(-) diff --git a/doc/rust.md b/doc/rust.md index b16c56be63361..964750dead72c 100644 --- a/doc/rust.md +++ b/doc/rust.md @@ -1222,7 +1222,7 @@ impl float: Num { let x: float = Num::from_int(42); ~~~~ -Traits can have _constraints_ for example, in +Traits may inherit from other traits. For example, in ~~~~ trait Shape { fn area() -> float; } @@ -1230,9 +1230,36 @@ trait Circle : Shape { fn radius() -> float; } ~~~~ the syntax `Circle : Shape` means that types that implement `Circle` must also have an implementation for `Shape`. +Multiple supertraits are separated by spaces, `trait Circle : Shape Eq { }`. In an implementation of `Circle` for a given type `T`, methods can refer to `Shape` methods, since the typechecker checks that any type with an implementation of `Circle` also has an implementation of `Shape`. +In type-parameterized functions, +methods of the supertrait may be called on values of subtrait-bound type parameters. +Refering to the previous example of `trait Circle : Shape`: + +~~~ +# trait Shape { fn area() -> float; } +# trait Circle : Shape { fn radius() -> float; } +fn radius_times_area(c: T) -> float { + // `c` is both a Circle and a Shape + c.radius() * c.area() +} +~~~ + +Likewise, supertrait methods may also be called on trait objects. + +~~~ {.xfail-test} +# trait Shape { fn area() -> float; } +# trait Circle : Shape { fn radius() -> float; } +# impl int: Shape { fn area() -> float { 0.0 } } +# impl int: Circle { fn radius() -> float { 0.0 } } +# let mycircle = 0; + +let mycircle: Circle = @mycircle as @Circle; +let nonsense = mycircle.radius() * mycircle.area(); +~~~ + ### Implementations An _implementation_ is an item that implements a [trait](#traits) for a specific type. diff --git a/doc/tutorial.md b/doc/tutorial.md index 6ea0aa67e4e1e..445b529159806 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -2118,10 +2118,10 @@ impl Circle: Shape { let s: Circle = Shape::new_shape(42.5); ~~~~ -## Trait constraints +## Trait inheritance -We can write a trait declaration that is _constrained_ to only be implementable on types that -also implement some other trait. +We can write a trait declaration that _inherits_ from other traits, called _supertraits_. +Types that implement a trait must also implement its supertraits. For example, we can define a `Circle` trait that only types that also have the `Shape` trait can have: @@ -2151,6 +2151,34 @@ impl CircleStruct: Shape { This is a silly way to compute the radius of a circle (since we could just return the `circle` field), but you get the idea. +In type-parameterized functions, +methods of the supertrait may be called on values of subtrait-bound type parameters. +Refering to the previous example of `trait Circle : Shape`: + +~~~ +# trait Shape { fn area() -> float; } +# trait Circle : Shape { fn radius() -> float; } +fn radius_times_area(c: T) -> float { + // `c` is both a Circle and a Shape + c.radius() * c.area() +} +~~~ + +Likewise, supertrait methods may also be called on trait objects. + +~~~ {.xfail-test} +# trait Shape { fn area() -> float; } +# trait Circle : Shape { fn radius() -> float; } +# impl int: Shape { fn area() -> float { 0.0 } } +# impl int: Circle { fn radius() -> float { 0.0 } } +# let mycircle = 0; + +let mycircle: Circle = @mycircle as @Circle; +let nonsense = mycircle.radius() * mycircle.area(); +~~~ + +> ***Note:*** Trait inheritance does not actually work with objects yet + ## Trait objects and dynamic method dispatch The above allows us to define functions that polymorphically act on From 8795e8b03476fd61366b6f9c0a77b75a12f51fe9 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Thu, 20 Dec 2012 00:33:15 -0800 Subject: [PATCH 02/26] tutorial: Reorder sections on traits /cc: #4217 --- doc/tutorial.md | 164 ++++++++++++++++++++++++------------------------ 1 file changed, 82 insertions(+), 82 deletions(-) diff --git a/doc/tutorial.md b/doc/tutorial.md index 445b529159806..812e46624aaf2 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -2092,6 +2092,88 @@ the preferred way to use traits polymorphically. This usage of traits is similar to Haskell type classes. +## Trait objects and dynamic method dispatch + +The above allows us to define functions that polymorphically act on +values of a single unknown type that conforms to a given trait. +However, consider this function: + +~~~~ +# type Circle = int; type Rectangle = int; +# impl int: Drawable { fn draw() {} } +# fn new_circle() -> int { 1 } +trait Drawable { fn draw(); } + +fn draw_all(shapes: ~[T]) { + for shapes.each |shape| { shape.draw(); } +} +# let c: Circle = new_circle(); +# draw_all(~[c]); +~~~~ + +You can call that on an array of circles, or an array of squares +(assuming those have suitable `Drawable` traits defined), but not on +an array containing both circles and squares. When such behavior is +needed, a trait name can alternately be used as a type, called +an _object_. + +~~~~ +# trait Drawable { fn draw(); } +fn draw_all(shapes: &[@Drawable]) { + for shapes.each |shape| { shape.draw(); } +} +~~~~ + +In this example, there is no type parameter. Instead, the `@Drawable` +type denotes any managed box value that implements the `Drawable` +trait. To construct such a value, you use the `as` operator to cast a +value to an object: + +~~~~ +# type Circle = int; type Rectangle = bool; +# trait Drawable { fn draw(); } +# fn new_circle() -> Circle { 1 } +# fn new_rectangle() -> Rectangle { true } +# fn draw_all(shapes: &[@Drawable]) {} + +impl Circle: Drawable { fn draw() { ... } } + +impl Rectangle: Drawable { fn draw() { ... } } + +let c: @Circle = @new_circle(); +let r: @Rectangle = @new_rectangle(); +draw_all([c as @Drawable, r as @Drawable]); +~~~~ + +We omit the code for `new_circle` and `new_rectangle`; imagine that +these just return `Circle`s and `Rectangle`s with a default size. Note +that, like strings and vectors, objects have dynamic size and may +only be referred to via one of the pointer types. +Other pointer types work as well. +Casts to traits may only be done with compatible pointers so, +for example, an `@Circle` may not be cast to an `~Drawable`. + +~~~ +# type Circle = int; type Rectangle = int; +# trait Drawable { fn draw(); } +# impl int: Drawable { fn draw() {} } +# fn new_circle() -> int { 1 } +# fn new_rectangle() -> int { 2 } +// A managed object +let boxy: @Drawable = @new_circle() as @Drawable; +// An owned object +let owny: ~Drawable = ~new_circle() as ~Drawable; +// A borrowed object +let stacky: &Drawable = &new_circle() as &Drawable; +~~~ + +Method calls to trait types are _dynamically dispatched_. Since the +compiler doesn't know specifically which functions to call at compile +time, it uses a lookup table (also known as a vtable or dictionary) to +select the method to call at runtime. + +This usage of traits is similar to Java interfaces. + ## Static methods Traits can define _static_ methods, which don't have an implicit `self` argument. @@ -2179,88 +2261,6 @@ let nonsense = mycircle.radius() * mycircle.area(); > ***Note:*** Trait inheritance does not actually work with objects yet -## Trait objects and dynamic method dispatch - -The above allows us to define functions that polymorphically act on -values of a single unknown type that conforms to a given trait. -However, consider this function: - -~~~~ -# type Circle = int; type Rectangle = int; -# impl int: Drawable { fn draw() {} } -# fn new_circle() -> int { 1 } -trait Drawable { fn draw(); } - -fn draw_all(shapes: ~[T]) { - for shapes.each |shape| { shape.draw(); } -} -# let c: Circle = new_circle(); -# draw_all(~[c]); -~~~~ - -You can call that on an array of circles, or an array of squares -(assuming those have suitable `Drawable` traits defined), but not on -an array containing both circles and squares. When such behavior is -needed, a trait name can alternately be used as a type, called -an _object_. - -~~~~ -# trait Drawable { fn draw(); } -fn draw_all(shapes: &[@Drawable]) { - for shapes.each |shape| { shape.draw(); } -} -~~~~ - -In this example, there is no type parameter. Instead, the `@Drawable` -type denotes any managed box value that implements the `Drawable` -trait. To construct such a value, you use the `as` operator to cast a -value to an object: - -~~~~ -# type Circle = int; type Rectangle = bool; -# trait Drawable { fn draw(); } -# fn new_circle() -> Circle { 1 } -# fn new_rectangle() -> Rectangle { true } -# fn draw_all(shapes: &[@Drawable]) {} - -impl Circle: Drawable { fn draw() { ... } } - -impl Rectangle: Drawable { fn draw() { ... } } - -let c: @Circle = @new_circle(); -let r: @Rectangle = @new_rectangle(); -draw_all([c as @Drawable, r as @Drawable]); -~~~~ - -We omit the code for `new_circle` and `new_rectangle`; imagine that -these just return `Circle`s and `Rectangle`s with a default size. Note -that, like strings and vectors, objects have dynamic size and may -only be referred to via one of the pointer types. -Other pointer types work as well. -Casts to traits may only be done with compatible pointers so, -for example, an `@Circle` may not be cast to an `~Drawable`. - -~~~ -# type Circle = int; type Rectangle = int; -# trait Drawable { fn draw(); } -# impl int: Drawable { fn draw() {} } -# fn new_circle() -> int { 1 } -# fn new_rectangle() -> int { 2 } -// A managed object -let boxy: @Drawable = @new_circle() as @Drawable; -// An owned object -let owny: ~Drawable = ~new_circle() as ~Drawable; -// A borrowed object -let stacky: &Drawable = &new_circle() as &Drawable; -~~~ - -Method calls to trait types are _dynamically dispatched_. Since the -compiler doesn't know specifically which functions to call at compile -time, it uses a lookup table (also known as a vtable or dictionary) to -select the method to call at runtime. - -This usage of traits is similar to Java interfaces. - # Modules and crates The Rust namespace is arranged in a hierarchy of modules. Each source From 4cc8877b97dd473e012fbc4e0a025c7118df7615 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Thu, 20 Dec 2012 01:34:15 -0800 Subject: [PATCH 03/26] tutorial: Rewrite method section to deal with explicit self Has to be moved until after the pointer discussion. /cc: #4217 --- doc/tutorial.md | 157 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 110 insertions(+), 47 deletions(-) diff --git a/doc/tutorial.md b/doc/tutorial.md index 812e46624aaf2..c799f54acc76f 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -918,7 +918,7 @@ match mytup { } ~~~~ -# Functions and methods +# Functions We've already seen several function definitions. Like all other static declarations, such as `type`, functions can be declared both at the @@ -968,52 +968,6 @@ assert 8 == line(5, 3, 1); assert () == oops(5, 3, 1); ~~~~ -Methods are like functions, except that they have an implicit argument -called `self`, which has the type that the method's receiver has. The -`self` argument is like 'this' in C++. An expression with dot -notation, as in `my_vec.len()`, denotes a method -call. Implementations, written with the `impl` keyword, can define -methods on most Rust types. As an example, let's define a `draw` -method on our `Shape` enum. - -~~~ -# fn draw_circle(p: Point, f: float) { } -# fn draw_rectangle(p: Point, p: Point) { } -struct Point { - x: float, - y: float -} - -enum Shape { - Circle(Point, float), - Rectangle(Point, Point) -} - -impl Shape { - fn draw() { - match self { - Circle(p, f) => draw_circle(p, f), - Rectangle(p1, p2) => draw_rectangle(p1, p2) - } - } -} - -let s = Circle(Point { x: 1f, y: 2f }, 3f); -s.draw(); -~~~ - -This defines an _implementation_ for `Shape` containing a single -method, `draw`. In most respects the `draw` method is defined -like any other function, except for the name `self`. `self` -is a special value that is automatically in scope inside each method, -referring to the value being operated on. If we wanted we could add -additional methods to the same impl, or multiple impls for the same -type. We'll discuss methods more in the context of [traits and -generics](#generics). - -> ***Note:*** In the future, the method definition syntax will change to -> require declaring the `self` type explicitly, as the first argument. - # The Rust memory model At this junction, let's take a detour to explain the concepts involved @@ -1526,6 +1480,115 @@ if favorite_crayon_name.len() > 5 { } ~~~ +# Methods + +Methods are like functions except that they always begin with a special argument, +called `self`, +which has the type of the method's receiver. The +`self` argument is like `this` in C++ and many other languages. +Methods are called with dot notation, as in `my_vec.len()`. + +_Implementations_, written with the `impl` keyword, can define +methods on most Rust types, including structs and enums. +As an example, let's define a `draw` method on our `Shape` enum. + +~~~ +# fn draw_circle(p: Point, f: float) { } +# fn draw_rectangle(p: Point, p: Point) { } +struct Point { + x: float, + y: float +} + +enum Shape { + Circle(Point, float), + Rectangle(Point, Point) +} + +impl Shape { + fn draw(&self) { + match *self { + Circle(p, f) => draw_circle(p, f), + Rectangle(p1, p2) => draw_rectangle(p1, p2) + } + } +} + +let s = Circle(Point { x: 1f, y: 2f }, 3f); +s.draw(); +~~~ + +This defines an _implementation_ for `Shape` containing a single +method, `draw`. In most respects the `draw` method is defined +like any other function, except for the name `self`. + +The type of `self` is the type on which the method is implemented, +or a pointer thereof. As an argument it is written either `self`, +`&self`, `@self`, or `~self`. +A caller must in turn have a compatible pointer type to call the method. + +~~~ +# fn draw_circle(p: Point, f: float) { } +# fn draw_rectangle(p: Point, p: Point) { } +# struct Point { x: float, y: float } +# enum Shape { +# Circle(Point, float), +# Rectangle(Point, Point) +# } +impl Shape { + fn draw_borrowed(&self) { ... } + fn draw_managed(@self) { ... } + fn draw_owned(~self) { ... } + fn draw_value(self) { ... } +} + +let s = Circle(Point { x: 1f, y: 2f }, 3f); + +(@s).draw_managed(); +(~s).draw_owned(); +(&s).draw_borrowed(); +s.draw_value(); +~~~ + +Methods typically take a borrowed pointer self type, +so the compiler will go to great lengths to convert a callee +to a borrowed pointer. + +~~~ +# fn draw_circle(p: Point, f: float) { } +# fn draw_rectangle(p: Point, p: Point) { } +# struct Point { x: float, y: float } +# enum Shape { +# Circle(Point, float), +# Rectangle(Point, Point) +# } +# impl Shape { +# fn draw_borrowed(&self) { ... } +# fn draw_managed(@self) { ... } +# fn draw_owned(~self) { ... } +# fn draw_value(self) { ... } +# } +# let s = Circle(Point { x: 1f, y: 2f }, 3f); +// As with typical function arguments, managed and unique pointers +// are automatically converted to borrowed pointers + +(@s).draw_borrowed(); +(~s).draw_borrowed(); + +// Unlike typical function arguments, the self value will +// automatically be referenced ... +s.draw_borrowed(); + +// ... and dereferenced +(& &s).draw_borrowed(); + +// ... and dereferenced, and borrowed, and +(&@~s).draw_borrowed(); +~~~ + +We'll discuss implementations more in the context of [traits and +generics](#generics). + # Closures Named functions, like those we've seen so far, may not refer to local From 68b80e4d799f82c0f02fd0d8ccaed71c00e0d520 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Thu, 20 Dec 2012 01:51:28 -0800 Subject: [PATCH 04/26] Use explicit self in rest of tutorial /cc: #4217 --- doc/tutorial.md | 89 +++++++++++++++++++++++++------------------------ 1 file changed, 45 insertions(+), 44 deletions(-) diff --git a/doc/tutorial.md b/doc/tutorial.md index c799f54acc76f..de3b2ed4a3a96 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -2033,7 +2033,7 @@ console, with a single method: ~~~~ trait Printable { - fn print(); + fn print(&self); } ~~~~ @@ -2045,13 +2045,13 @@ and `~str`. [impls]: #functions-and-methods ~~~~ -# trait Printable { fn print(); } +# trait Printable { fn print(&self); } impl int: Printable { - fn print() { io::println(fmt!("%d", self)) } + fn print(&self) { io::println(fmt!("%d", *self)) } } impl &str: Printable { - fn print() { io::println(self) } + fn print(&self) { io::println(*self) } } # 1.print(); @@ -2065,14 +2065,14 @@ types might look like the following: ~~~~ trait Seq { - fn len() -> uint; - fn iter(b: fn(v: &T)); + fn len(&self) -> uint; + fn iter(&self, b: fn(v: &T)); } impl ~[T]: Seq { - fn len() -> uint { vec::len(self) } - fn iter(b: fn(v: &T)) { - for vec::each(self) |elt| { b(elt); } + fn len(&self) -> uint { vec::len(*self) } + fn iter(&self, b: fn(v: &T)) { + for vec::each(*self) |elt| { b(elt); } } } ~~~~ @@ -2096,21 +2096,22 @@ trait, `self` is a type, and in an impl, `self` is a value. The following trait describes types that support an equality operation: ~~~~ -// In a trait, `self` refers to the type implementing the trait +// In a trait, `self` refers both to the self argument +// and to the type implementing the trait trait Eq { - fn equals(other: &self) -> bool; + fn equals(&self, other: &self) -> bool; } -// In an impl, `self` refers to the value of the receiver +// In an impl, `self` refers just to the value of the receiver impl int: Eq { - fn equals(other: &int) -> bool { *other == self } + fn equals(&self, other: &int) -> bool { *other == *self } } ~~~~ -Notice that in the trait definition, `equals` takes a parameter of -type `self`. In contrast, in the `impl`, `equals` takes a parameter of -type `int`, and uses `self` as the name of the receiver (analogous to -the `this` pointer in C++). +Notice that in the trait definition, `equals` takes a +second parameter of type `self`. +In contrast, in the `impl`, `equals` takes a second parameter of +type `int`, only using `self` as the name of the receiver. ## Bounded type parameters and static method dispatch @@ -2120,7 +2121,7 @@ define _bounds_ on type parameters, so that we can then operate on generic types. ~~~~ -# trait Printable { fn print(); } +# trait Printable { fn print(&self); } fn print_all(printable_things: ~[T]) { for printable_things.each |thing| { thing.print(); @@ -2138,7 +2139,7 @@ Type parameters can have multiple bounds by separating them with spaces, as in this version of `print_all` that copies elements. ~~~ -# trait Printable { fn print(); } +# trait Printable { fn print(&self); } fn print_all(printable_things: ~[T]) { let mut i = 0; while i < printable_things.len() { @@ -2163,9 +2164,9 @@ However, consider this function: ~~~~ # type Circle = int; type Rectangle = int; -# impl int: Drawable { fn draw() {} } +# impl int: Drawable { fn draw(&self) {} } # fn new_circle() -> int { 1 } -trait Drawable { fn draw(); } +trait Drawable { fn draw(&self); } fn draw_all(shapes: ~[T]) { for shapes.each |shape| { shape.draw(); } @@ -2181,7 +2182,7 @@ needed, a trait name can alternately be used as a type, called an _object_. ~~~~ -# trait Drawable { fn draw(); } +# trait Drawable { fn draw(&self); } fn draw_all(shapes: &[@Drawable]) { for shapes.each |shape| { shape.draw(); } } @@ -2194,14 +2195,14 @@ value to an object: ~~~~ # type Circle = int; type Rectangle = bool; -# trait Drawable { fn draw(); } +# trait Drawable { fn draw(&self); } # fn new_circle() -> Circle { 1 } # fn new_rectangle() -> Rectangle { true } # fn draw_all(shapes: &[@Drawable]) {} -impl Circle: Drawable { fn draw() { ... } } +impl Circle: Drawable { fn draw(&self) { ... } } -impl Rectangle: Drawable { fn draw() { ... } } +impl Rectangle: Drawable { fn draw(&self) { ... } } let c: @Circle = @new_circle(); let r: @Rectangle = @new_rectangle(); @@ -2218,8 +2219,8 @@ for example, an `@Circle` may not be cast to an `~Drawable`. ~~~ # type Circle = int; type Rectangle = int; -# trait Drawable { fn draw(); } -# impl int: Drawable { fn draw() {} } +# trait Drawable { fn draw(&self); } +# impl int: Drawable { fn draw(&self) {} } # fn new_circle() -> int { 1 } # fn new_rectangle() -> int { 2 } // A managed object @@ -2244,7 +2245,7 @@ The `static` keyword distinguishes static methods from methods that have a `self ~~~~ trait Shape { - fn area() -> float; + fn area(&self) -> float; static fn new_shape(area: float) -> Shape; } ~~~~ @@ -2271,25 +2272,25 @@ Types that implement a trait must also implement its supertraits. For example, we can define a `Circle` trait that only types that also have the `Shape` trait can have: ~~~~ -trait Shape { fn area() -> float; } -trait Circle : Shape { fn radius() -> float; } +trait Shape { fn area(&self) -> float; } +trait Circle : Shape { fn radius(&self) -> float; } ~~~~ Now, implementations of `Circle` methods can call `Shape` methods: ~~~~ -# trait Shape { fn area() -> float; } -# trait Circle : Shape { fn radius() -> float; } +# trait Shape { fn area(&self) -> float; } +# trait Circle : Shape { fn radius(&self) -> float; } # struct Point { x: float, y: float } # use float::consts::pi; # use float::sqrt; # fn square(x: float) -> float { x * x } struct CircleStruct { center: Point, radius: float } impl CircleStruct: Circle { - fn radius() -> float { sqrt(self.area() / pi) } + fn radius(&self) -> float { sqrt(self.area() / pi) } } impl CircleStruct: Shape { - fn area() -> float { pi * square(self.radius) } + fn area(&self) -> float { pi * square(self.radius) } } ~~~~ @@ -2301,8 +2302,8 @@ methods of the supertrait may be called on values of subtrait-bound type paramet Refering to the previous example of `trait Circle : Shape`: ~~~ -# trait Shape { fn area() -> float; } -# trait Circle : Shape { fn radius() -> float; } +# trait Shape { fn area(&self) -> float; } +# trait Circle : Shape { fn radius(&self) -> float; } fn radius_times_area(c: T) -> float { // `c` is both a Circle and a Shape c.radius() * c.area() @@ -2312,10 +2313,10 @@ fn radius_times_area(c: T) -> float { Likewise, supertrait methods may also be called on trait objects. ~~~ {.xfail-test} -# trait Shape { fn area() -> float; } -# trait Circle : Shape { fn radius() -> float; } -# impl int: Shape { fn area() -> float { 0.0 } } -# impl int: Circle { fn radius() -> float { 0.0 } } +# trait Shape { fn area(&self) -> float; } +# trait Circle : Shape { fn radius(&self) -> float; } +# impl int: Shape { fn area(&self) -> float { 0.0 } } +# impl int: Circle { fn radius(&self) -> float { 0.0 } } # let mycircle = 0; let mycircle: Circle = @mycircle as @Circle; @@ -2385,9 +2386,9 @@ mod farm { // Note - visibility modifiers on impls currently have no effect impl Farm { - priv fn feed_chickens() { ... } - priv fn feed_cows() { ... } - fn add_chicken(c: Chicken) { ... } + priv fn feed_chickens(&self) { ... } + priv fn feed_cows(&self) { ... } + fn add_chicken(&self, c: Chicken) { ... } } pub fn feed_animals(farm: &Farm) { @@ -2407,7 +2408,7 @@ fn main() { # enum Human = int; # fn make_me_a_farm() -> farm::Farm { farm::make_me_a_farm() } # fn make_me_a_chicken() -> Chicken { 0 } -# impl Human { fn rest() { } } +# impl Human { fn rest(&self) { } } ~~~ ## Crates From cad30a2b67a32241d5e32689b4e4c328e9521f47 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Thu, 20 Dec 2012 02:15:59 -0800 Subject: [PATCH 05/26] Move mast static method to the section on methods /cc: #4217 --- doc/tutorial.md | 79 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 53 insertions(+), 26 deletions(-) diff --git a/doc/tutorial.md b/doc/tutorial.md index de3b2ed4a3a96..a0fa95d31be9e 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -1586,6 +1586,36 @@ s.draw_borrowed(); (&@~s).draw_borrowed(); ~~~ +Implementations may also define _static_ methods, +which don't have an explicit `self` argument. +The `static` keyword distinguishes static methods from methods that have a `self`: + +~~~~ {.xfail-test} +impl Circle { + fn area(&self) -> float { ... } + static fn new(area: float) -> Circle { ... } +} +~~~~ + +> ***Note***: In the future the `static` keyword will be removed and static methods +> will be distinguished solely by the presence or absence of the `self` argument. +> In the current langugage instance methods may also be declared without an explicit +> `self` argument, in which case `self` is an implicit reference. +> That form of method is deprecated. + +Constructors are one common application for static methods, as in `new` above. +To call a static method, you have to prefix it with the type name and a double colon: + +~~~~ +# use float::consts::pi; +# use float::sqrt; +struct Circle { radius: float } +impl Circle { + static fn new(area: float) -> Circle { Circle { radius: sqrt(area / pi) } } +} +let c = Circle::new(42.5); +~~~~ + We'll discuss implementations more in the context of [traits and generics](#generics). @@ -2113,6 +2143,29 @@ second parameter of type `self`. In contrast, in the `impl`, `equals` takes a second parameter of type `int`, only using `self` as the name of the receiver. +Traits can also define static methods which are called by prefixing +the method name with the trait name. +The compiler will use type inference to decide which implementation to call. + +~~~~ +# trait Shape { static fn new(area: float) -> self; } +# use float::consts::pi; +# use float::sqrt; +struct Circle { radius: float } +struct Square { length: float } + +impl Circle: Shape { + static fn new(area: float) -> Circle { Circle { radius: sqrt(area / pi) } } +} +impl Square: Shape { + static fn new(area: float) -> Square { Square { length: sqrt(area) } } +} + +let area = 42.5; +let c: Circle = Shape::new(area); +let s: Square = Shape::new(area); +~~~~ + ## Bounded type parameters and static method dispatch Traits give us a language for defining predicates on types, or @@ -2238,32 +2291,6 @@ select the method to call at runtime. This usage of traits is similar to Java interfaces. -## Static methods - -Traits can define _static_ methods, which don't have an implicit `self` argument. -The `static` keyword distinguishes static methods from methods that have a `self`: - -~~~~ -trait Shape { - fn area(&self) -> float; - static fn new_shape(area: float) -> Shape; -} -~~~~ - -Constructors are one application for static methods, as in `new_shape` above. -To call a static method, you have to prefix it with the trait name and a double colon: - -~~~~ -# trait Shape { static fn new_shape(area: float) -> self; } -# use float::consts::pi; -# use float::sqrt; -struct Circle { radius: float } -impl Circle: Shape { - static fn new_shape(area: float) -> Circle { Circle { radius: sqrt(area / pi) } } -} -let s: Circle = Shape::new_shape(42.5); -~~~~ - ## Trait inheritance We can write a trait declaration that _inherits_ from other traits, called _supertraits_. From 0617708014339b2c1511b42bb11c6290efa68478 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Thu, 20 Dec 2012 02:22:05 -0800 Subject: [PATCH 06/26] tutorial: Move method discussion after closures, before generics /cc: #4217 --- doc/tutorial.md | 275 ++++++++++++++++++++++++------------------------ 1 file changed, 136 insertions(+), 139 deletions(-) diff --git a/doc/tutorial.md b/doc/tutorial.md index a0fa95d31be9e..b9ad8900a4fca 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -1480,145 +1480,6 @@ if favorite_crayon_name.len() > 5 { } ~~~ -# Methods - -Methods are like functions except that they always begin with a special argument, -called `self`, -which has the type of the method's receiver. The -`self` argument is like `this` in C++ and many other languages. -Methods are called with dot notation, as in `my_vec.len()`. - -_Implementations_, written with the `impl` keyword, can define -methods on most Rust types, including structs and enums. -As an example, let's define a `draw` method on our `Shape` enum. - -~~~ -# fn draw_circle(p: Point, f: float) { } -# fn draw_rectangle(p: Point, p: Point) { } -struct Point { - x: float, - y: float -} - -enum Shape { - Circle(Point, float), - Rectangle(Point, Point) -} - -impl Shape { - fn draw(&self) { - match *self { - Circle(p, f) => draw_circle(p, f), - Rectangle(p1, p2) => draw_rectangle(p1, p2) - } - } -} - -let s = Circle(Point { x: 1f, y: 2f }, 3f); -s.draw(); -~~~ - -This defines an _implementation_ for `Shape` containing a single -method, `draw`. In most respects the `draw` method is defined -like any other function, except for the name `self`. - -The type of `self` is the type on which the method is implemented, -or a pointer thereof. As an argument it is written either `self`, -`&self`, `@self`, or `~self`. -A caller must in turn have a compatible pointer type to call the method. - -~~~ -# fn draw_circle(p: Point, f: float) { } -# fn draw_rectangle(p: Point, p: Point) { } -# struct Point { x: float, y: float } -# enum Shape { -# Circle(Point, float), -# Rectangle(Point, Point) -# } -impl Shape { - fn draw_borrowed(&self) { ... } - fn draw_managed(@self) { ... } - fn draw_owned(~self) { ... } - fn draw_value(self) { ... } -} - -let s = Circle(Point { x: 1f, y: 2f }, 3f); - -(@s).draw_managed(); -(~s).draw_owned(); -(&s).draw_borrowed(); -s.draw_value(); -~~~ - -Methods typically take a borrowed pointer self type, -so the compiler will go to great lengths to convert a callee -to a borrowed pointer. - -~~~ -# fn draw_circle(p: Point, f: float) { } -# fn draw_rectangle(p: Point, p: Point) { } -# struct Point { x: float, y: float } -# enum Shape { -# Circle(Point, float), -# Rectangle(Point, Point) -# } -# impl Shape { -# fn draw_borrowed(&self) { ... } -# fn draw_managed(@self) { ... } -# fn draw_owned(~self) { ... } -# fn draw_value(self) { ... } -# } -# let s = Circle(Point { x: 1f, y: 2f }, 3f); -// As with typical function arguments, managed and unique pointers -// are automatically converted to borrowed pointers - -(@s).draw_borrowed(); -(~s).draw_borrowed(); - -// Unlike typical function arguments, the self value will -// automatically be referenced ... -s.draw_borrowed(); - -// ... and dereferenced -(& &s).draw_borrowed(); - -// ... and dereferenced, and borrowed, and -(&@~s).draw_borrowed(); -~~~ - -Implementations may also define _static_ methods, -which don't have an explicit `self` argument. -The `static` keyword distinguishes static methods from methods that have a `self`: - -~~~~ {.xfail-test} -impl Circle { - fn area(&self) -> float { ... } - static fn new(area: float) -> Circle { ... } -} -~~~~ - -> ***Note***: In the future the `static` keyword will be removed and static methods -> will be distinguished solely by the presence or absence of the `self` argument. -> In the current langugage instance methods may also be declared without an explicit -> `self` argument, in which case `self` is an implicit reference. -> That form of method is deprecated. - -Constructors are one common application for static methods, as in `new` above. -To call a static method, you have to prefix it with the type name and a double colon: - -~~~~ -# use float::consts::pi; -# use float::sqrt; -struct Circle { radius: float } -impl Circle { - static fn new(area: float) -> Circle { Circle { radius: sqrt(area / pi) } } -} -let c = Circle::new(42.5); -~~~~ - -We'll discuss implementations more in the context of [traits and -generics](#generics). - # Closures Named functions, like those we've seen so far, may not refer to local @@ -1886,6 +1747,142 @@ fn contains(v: &[int], elt: int) -> bool { > the keywords `break`, `loop`, and `return` work, in varying degree, > with `while`, `loop`, `do`, and `for` constructs. +# Methods + +Methods are like functions except that they always begin with a special argument, +called `self`, +which has the type of the method's receiver. The +`self` argument is like `this` in C++ and many other languages. +Methods are called with dot notation, as in `my_vec.len()`. + +_Implementations_, written with the `impl` keyword, can define +methods on most Rust types, including structs and enums. +As an example, let's define a `draw` method on our `Shape` enum. + +~~~ +# fn draw_circle(p: Point, f: float) { } +# fn draw_rectangle(p: Point, p: Point) { } +struct Point { + x: float, + y: float +} + +enum Shape { + Circle(Point, float), + Rectangle(Point, Point) +} + +impl Shape { + fn draw(&self) { + match *self { + Circle(p, f) => draw_circle(p, f), + Rectangle(p1, p2) => draw_rectangle(p1, p2) + } + } +} + +let s = Circle(Point { x: 1f, y: 2f }, 3f); +s.draw(); +~~~ + +This defines an _implementation_ for `Shape` containing a single +method, `draw`. In most respects the `draw` method is defined +like any other function, except for the name `self`. + +The type of `self` is the type on which the method is implemented, +or a pointer thereof. As an argument it is written either `self`, +`&self`, `@self`, or `~self`. +A caller must in turn have a compatible pointer type to call the method. + +~~~ +# fn draw_circle(p: Point, f: float) { } +# fn draw_rectangle(p: Point, p: Point) { } +# struct Point { x: float, y: float } +# enum Shape { +# Circle(Point, float), +# Rectangle(Point, Point) +# } +impl Shape { + fn draw_borrowed(&self) { ... } + fn draw_managed(@self) { ... } + fn draw_owned(~self) { ... } + fn draw_value(self) { ... } +} + +let s = Circle(Point { x: 1f, y: 2f }, 3f); + +(@s).draw_managed(); +(~s).draw_owned(); +(&s).draw_borrowed(); +s.draw_value(); +~~~ + +Methods typically take a borrowed pointer self type, +so the compiler will go to great lengths to convert a callee +to a borrowed pointer. + +~~~ +# fn draw_circle(p: Point, f: float) { } +# fn draw_rectangle(p: Point, p: Point) { } +# struct Point { x: float, y: float } +# enum Shape { +# Circle(Point, float), +# Rectangle(Point, Point) +# } +# impl Shape { +# fn draw_borrowed(&self) { ... } +# fn draw_managed(@self) { ... } +# fn draw_owned(~self) { ... } +# fn draw_value(self) { ... } +# } +# let s = Circle(Point { x: 1f, y: 2f }, 3f); +// As with typical function arguments, managed and unique pointers +// are automatically converted to borrowed pointers + +(@s).draw_borrowed(); +(~s).draw_borrowed(); + +// Unlike typical function arguments, the self value will +// automatically be referenced ... +s.draw_borrowed(); + +// ... and dereferenced +(& &s).draw_borrowed(); + +// ... and dereferenced, and borrowed, and +(&@~s).draw_borrowed(); +~~~ + +Implementations may also define _static_ methods, +which don't have an explicit `self` argument. +The `static` keyword distinguishes static methods from methods that have a `self`: + +~~~~ {.xfail-test} +impl Circle { + fn area(&self) -> float { ... } + static fn new(area: float) -> Circle { ... } +} +~~~~ + +> ***Note***: In the future the `static` keyword will be removed and static methods +> will be distinguished solely by the presence or absence of the `self` argument. +> In the current langugage instance methods may also be declared without an explicit +> `self` argument, in which case `self` is an implicit reference. +> That form of method is deprecated. + +Constructors are one common application for static methods, as in `new` above. +To call a static method, you have to prefix it with the type name and a double colon: + +~~~~ +# use float::consts::pi; +# use float::sqrt; +struct Circle { radius: float } +impl Circle { + static fn new(area: float) -> Circle { Circle { radius: sqrt(area / pi) } } +} +let c = Circle::new(42.5); +~~~~ + # Generics Throughout this tutorial, we've been defining functions that act only From d4688931abd80330dc03591ef8b707e279dc7acf Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Thu, 20 Dec 2012 02:22:56 -0800 Subject: [PATCH 07/26] tutorial: Typos --- doc/tutorial.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/tutorial.md b/doc/tutorial.md index b9ad8900a4fca..9fc1e67e82bbd 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -1956,8 +1956,8 @@ fn radius(shape: Shape) -> Option { The Rust compiler compiles generic functions very efficiently by *monomorphizing* them. *Monomorphization* is a fancy name for a simple -idea: generate a separate copy of each generic function at each call -site where it is called, a copy that is specialized to the argument +idea: generate a separate copy of each generic function at each call site, +a copy that is specialized to the argument types and can thus be optimized specifically for them. In this respect, Rust's generics have similar performance characteristics to C++ templates. From 0a7bd7879a29ce1e19e01df0cd25b79fb49f74d2 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Thu, 20 Dec 2012 02:37:21 -0800 Subject: [PATCH 08/26] tutorial: Update for moves based on type /cc: #4217 --- doc/tutorial.md | 28 +++++++++------------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/doc/tutorial.md b/doc/tutorial.md index 9fc1e67e82bbd..11fcf360e4609 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -1124,13 +1124,16 @@ _exchange heap_, where their uniquely-owned nature allows tasks to exchange them efficiently. Because owned boxes are uniquely owned, copying them requires allocating -a new owned box and duplicating the contents. Copying owned boxes -is expensive so the compiler will complain if you do so without writing -the word `copy`. +a new owned box and duplicating the contents. +Instead, owned boxes are _moved_ by default, transferring ownership, +and deinitializing the previously owning variable. +Any attempt to access a variable after the value has been moved out +will result in a compile error. ~~~~ let x = ~10; -let y = x; // error: copying a non-implicitly copyable type +// Move x to y, deinitializing x +let y = x; ~~~~ If you really want to copy an owned box you must say so explicitly. @@ -1143,19 +1146,6 @@ let z = *x + *y; assert z == 20; ~~~~ -This is where the 'move' operator comes in. It is similar to `copy`, -but it de-initializes its source. Thus, the owned box can move from -`x` to `y`, without violating the constraint that it only has a single -owner (using assignment instead of the move operator would, in -principle, copy the box). - -~~~~ {.xfail-test} -let x = ~10; -let y = move x; - -let z = *x + *y; // would cause an error: use of moved variable: `x` -~~~~ - Owned boxes, when they do not contain any managed boxes, can be sent to other tasks. The sending task will give up ownership of the box, and won't be able to access it afterwards. The receiving task will @@ -1360,7 +1350,7 @@ let your_crayons = ~[BananaMania, Beaver, Bittersweet]; let our_crayons = my_crayons + your_crayons; // += will append to a vector, provided it lives in a mutable slot -let mut my_crayons = move my_crayons; +let mut my_crayons = my_crayons; my_crayons += your_crayons; ~~~~ @@ -1899,7 +1889,7 @@ fn map(vector: &[T], function: fn(v: &T) -> U) -> ~[U] { for vec::each(vector) |element| { accumulator.push(function(element)); } - return (move accumulator); + return accumulator; } ~~~~ From 7ec45b6bed208e7bd03c2f7ca4b9a52ce93ea05e Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Thu, 20 Dec 2012 02:41:17 -0800 Subject: [PATCH 09/26] tutorial: update intro --- doc/tutorial.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/tutorial.md b/doc/tutorial.md index 11fcf360e4609..4506d472e1dda 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -128,7 +128,7 @@ we have a file `hello.rs` containing this program: ~~~~ fn main() { - io::println("hello? yes, this is rust"); + io::println("hello?"); } ~~~~ @@ -143,7 +143,7 @@ an error message like this: ~~~~ {.notrust} hello.rs:2:4: 2:16 error: unresolved name: io::print_with_unicorns -hello.rs:2 io::print_with_unicorns("hello? yes, this is rust"); +hello.rs:2 io::print_with_unicorns("hello?"); ^~~~~~~~~~~~~~~~~~~~~~~ ~~~~ From 91895adf83e3552a5b6f4ea8346abfb476bec8ec Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Thu, 20 Dec 2012 02:46:34 -0800 Subject: [PATCH 10/26] tutorial: Fix formatting --- doc/tutorial.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/tutorial.md b/doc/tutorial.md index 4506d472e1dda..3dfa34d2664a6 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -216,7 +216,7 @@ while count < 10 { The name of the function that prints a line of text, `io::println`, is qualified: it refers to the function named `println` that's defined in the -module `io`. In Rust, a double colon---`::`---separates parts of a +module `io`. In Rust, a double colon separates parts of a qualified name. For more details, see the section on [crates](#crates). Although Rust can almost always infer the types of local variables, you From 614629adde4fa9e596456b167b40f797e15f5066 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Thu, 20 Dec 2012 02:54:21 -0800 Subject: [PATCH 11/26] tutorial: Remove confusing discussion about semicolons This takes up a lot of words and isn't very clear. The previous discussion gets they idea across. /cc: #4217 --- doc/tutorial.md | 40 ---------------------------------------- 1 file changed, 40 deletions(-) diff --git a/doc/tutorial.md b/doc/tutorial.md index 3dfa34d2664a6..8a246f897a0be 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -308,46 +308,6 @@ fn is_four(x: int) -> bool { } ~~~~ -If all those things are expressions, you might conclude that you have -to add a terminating semicolon after *every* statement, even ones that -are not traditionally terminated with a semicolon in C (like `while`). -That is not the case, though. Expressions that end in a block only -need a semicolon if that block contains a trailing expression. `while` -loops do not allow trailing expressions, and `if` statements tend to -only have a trailing expression when you want to use their value for -something—in which case you'll have embedded it in a bigger statement. - -~~~ -# fn foo() -> bool { true } -# fn bar() -> bool { true } -# fn baz() -> bool { true } -// `let` is not an expression, so it is semicolon-terminated; -let x = foo(); - -// When used in statement position, bracy expressions do not -// usually need to be semicolon terminated -if x { - bar(); -} else { - baz(); -} // No semi-colon - -// Although, if `bar` and `baz` have non-nil return types, and -// we try to use them as the tail expressions, rustc will -// make us terminate the expression. -if x { - bar() -} else { - baz() -}; // Semi-colon to ignore non-nil block type - -// An `if` embedded in `let` again requires a semicolon to terminate -// the `let` statement -let y = if x { foo() } else { bar() }; -~~~ - -This may sound intricate, but it is super-useful and will grow on you. - ## Types The basic types include the usual boolean, integral, and floating-point types. From ecd1aa75eb2bc64d65596b90e2ff8841ae63d2ea Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Thu, 20 Dec 2012 03:03:08 -0800 Subject: [PATCH 12/26] tutorial: Remove mutable vector syntax --- doc/tutorial.md | 1 - 1 file changed, 1 deletion(-) diff --git a/doc/tutorial.md b/doc/tutorial.md index 8a246f897a0be..d3d1a515d8e9b 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -329,7 +329,6 @@ while N should be a literal number): ------------------------- ----------------------------------------------- `[T * N]` Vector (like an array in other languages) with N elements -`[mut T * N]` Mutable vector with N elements `(T1, T2)` Tuple type; any arity above 1 is supported `&T`, `~T`, `@T` [Pointer types](#boxes-and-pointers) ------------------------- ----------------------------------------------- From 4855ce1731f8712c059394619dbefa8a8e6131bc Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Thu, 20 Dec 2012 03:07:34 -0800 Subject: [PATCH 13/26] tutorial: Remove the entire 'Types' section It's not interesting /cc: #4217 --- doc/tutorial.md | 81 ------------------------------------------------- 1 file changed, 81 deletions(-) diff --git a/doc/tutorial.md b/doc/tutorial.md index d3d1a515d8e9b..5ac91438dc33c 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -308,87 +308,6 @@ fn is_four(x: int) -> bool { } ~~~~ -## Types - -The basic types include the usual boolean, integral, and floating-point types. - -------------------------- ----------------------------------------------- -`()` Unit, the type that has only a single value -`bool` Boolean type, with values `true` and `false` -`int`, `uint` Machine-pointer-sized signed and unsigned integers -`i8`, `i16`, `i32`, `i64` Signed integers with a specific size (in bits) -`u8`, `u16`, `u32`, `u64` Unsigned integers with a specific size -`float` The largest floating-point type efficiently supported on the target machine -`f32`, `f64` Floating-point types with a specific size -`char` A Unicode character (32 bits) -------------------------- ----------------------------------------------- - -These can be combined in composite types, which will be described in -more detail later on (the `T`s here stand for any other type, -while N should be a literal number): - -------------------------- ----------------------------------------------- -`[T * N]` Vector (like an array in other languages) with N elements -`(T1, T2)` Tuple type; any arity above 1 is supported -`&T`, `~T`, `@T` [Pointer types](#boxes-and-pointers) -------------------------- ----------------------------------------------- - -Some types can only be manipulated by pointer, never directly. For instance, -you cannot refer to a string (`str`); instead you refer to a pointer to a -string (`@str`, `~str`, or `&str`). These *dynamically-sized* types consist -of: - -------------------------- ----------------------------------------------- -`fn(a: T1, b: T2) -> T3` Function types -`str` String type (in UTF-8) -`[T]` Vector with unknown size (also called a slice) -`[mut T]` Mutable vector with unknown size -------------------------- ----------------------------------------------- - -> ***Note***: In the future, mutability for vectors may be defined by -> the slot that contains the vector, not the type of the vector itself, -> deprecating [mut T] syntax. - -In function types, the return type is specified with an arrow, as in -the type `fn() -> bool` or the function declaration `fn foo() -> bool -{ }`. For functions that do not return a meaningful value, you can -optionally write `-> ()`, but usually the return annotation is simply -left off, as in `fn main() { ... }`. - -Types can be given names or aliases with `type` declarations: - -~~~~ -type MonsterSize = uint; -~~~~ - -This will provide a synonym, `MonsterSize`, for unsigned integers. It will not -actually create a new, incompatible type—`MonsterSize` and `uint` can be used -interchangeably, and using one where the other is expected is not a type -error. In that sense, types declared with `type` are *structural*: their -meaning follows from their structure, and their names are irrelevant in the -type system. - -Sometimes, you want your data types to be *nominal* instead of structural: you -want their name to be part of their meaning, so that types with the same -structure but different names are not interchangeable. Rust has two ways to -create nominal data types: `struct` and `enum`. They're described in more -detail below, but they look like this: - -~~~~ -enum HidingPlaces { - Closet(uint), - UnderTheBed(uint) -} - -struct HeroicBabysitter { - bedtime_stories: uint, - sharpened_stakes: uint -} - -struct BabysitterSize(uint); // a single-variant struct -enum MonsterSize = uint; // a single-variant enum -~~~~ - ## Literals Integers can be written in decimal (`144`), hexadecimal (`0x90`), or From 8ea768219b8570b7a765ee7b0a52696fddb55f33 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Thu, 20 Dec 2012 03:09:51 -0800 Subject: [PATCH 14/26] tutorial: Remove the section on constants We can mention that constants are declared with 'const' in one line. Don't need an entire section. --- doc/tutorial.md | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/doc/tutorial.md b/doc/tutorial.md index 5ac91438dc33c..6fc379b6c6735 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -342,34 +342,6 @@ character, such as `\n`, `\r`, and `\t`. String literals, written between double quotes, allow the same escape sequences. Rust strings may contain newlines. -## Constants - -Compile-time constants are declared with `const`. A constant may have any -scalar type (for example, integer or float). Other allowable constant types -are fixed-length vectors, static strings (more on this later), and -structs. Constants may be declared in any scope and may refer to other -constants. The compiler does not infer types for constants, so constants must -always be declared with a type annotation. By convention, they are written in -all capital letters. - -~~~ -// Scalars can be constants -const MY_PASSWORD: int = 12345; - -// Scalar constants can be combined with other constants -const MY_DOGGIES_PASSWORD: int = MY_PASSWORD + 1; - -// Fixed-length vectors -const MY_VECTORY_PASSWORD: [int * 5] = [1, 2, 3, 4, 5]; - -// Static strings -const MY_STRINGY_PASSWORD: &static/str = "12345"; - -// Structs -struct Password { value: int } -const MY_STRUCTY_PASSWORD: Password = Password { value: MY_PASSWORD }; -~~~ - ## Operators Rust's set of operators contains very few surprises. Arithmetic is done with From 685b6fbc7148fb714bfa3f6ef1d8075926645f43 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Thu, 20 Dec 2012 03:18:11 -0800 Subject: [PATCH 15/26] tutorial: Integrate constants into main intro text --- doc/tutorial.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/tutorial.md b/doc/tutorial.md index 6fc379b6c6735..5132cdd601489 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -221,11 +221,11 @@ qualified name. For more details, see the section on [crates](#crates). Although Rust can almost always infer the types of local variables, you can specify a variable's type by following it with a colon, then the type -name. +name. Constants, an the other hand, always require a type annotation. ~~~~ -let monster_size: float = 57.8; -let imaginary_size = monster_size * 10.0; +const monster_factor: float = 57.8; +let monster_size: float = monster_factor * 10.0; let monster_size: int = 50; ~~~~ From 41aee4d36d63ff5328667b49b7c54ff0b5d18ee2 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Thu, 20 Dec 2012 03:21:04 -0800 Subject: [PATCH 16/26] tutorial: It doesn't matter that Rust identifiers are the same as C --- doc/tutorial.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/tutorial.md b/doc/tutorial.md index 5132cdd601489..5ea37accb4984 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -238,7 +238,7 @@ programmer error). For occasions where unused variables are intentional, their name may be prefixed with an underscore to silence the warning, like `let _monster_size = 50;`. -Rust identifiers follow the same rules as C; they start with an alphabetic +Rust identifiers start with an alphabetic character or an underscore, and after that may contain any sequence of alphabetic characters, numbers, or underscores. The preferred style is to begin function, variable, and module names with a lowercase letter, using From 42f37e009728ecbef9f30c84014223fa2696db55 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Thu, 20 Dec 2012 03:43:20 -0800 Subject: [PATCH 17/26] tutorial: Discuss the primitive types along with their literals --- doc/tutorial.md | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/doc/tutorial.md b/doc/tutorial.md index 5ea37accb4984..0a50bcc45b495 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -249,7 +249,7 @@ let my_variable = 100; type MyType = int; // some built-in types are _not_ camel case ~~~ -## Expression syntax +## Expressions and semicolons Though it isn't apparent in all code, there is a fundamental difference between Rust's syntax and predecessors like C. @@ -308,12 +308,14 @@ fn is_four(x: int) -> bool { } ~~~~ -## Literals +## Primitive types and literals +There are general signed and unsigned integer types, `int`, and `uint`, +as well as 8-, 16-, 32-, and 64-bit variations, `i8`, `u16`, etc. Integers can be written in decimal (`144`), hexadecimal (`0x90`), or binary (`0b10010000`) base. Each integral type has a corresponding literal suffix that can be used to indicate the type of a literal: `i` for `int`, -`u` for `uint`, and `i8` for the `i8` type, etc. +`u` for `uint`, `i8` for the `i8` type. In the absence of an integer literal suffix, Rust will infer the integer type based on type annotations and function signatures in the @@ -328,19 +330,21 @@ let c = 100u; // c is a uint let d = 1000i32; // d is an i32 ~~~~ -Floating point numbers are written `0.0`, `1e6`, or `2.1e-4`. Without -a suffix, the literal is assumed to be of type `float`. Suffixes `f32` -(32-bit) and `f64` (64-bit) can be used to create literals of a -specific type. +There are three floating point types, `float`, `f32`, and `f64`. +Floating point numbers are written `0.0`, `1e6`, or `2.1e-4`. +Like integers, floating point literals are inferred to the correct type. +Suffixes `f`, `f32` and `f64` can be used to create literals of a specific type. -The unit literal is written just like the type: `()`. The keywords -`true` and `false` produce the boolean literals. +The keywords `true` and `false` produce literals of type `bool`. -Character literals are written between single quotes, as in `'x'`. Just like -C, Rust understands a number of character escapes, using the backslash +Characters, the `char` type, are 4-byte unicode codepoints, +whose literals are written between single quotes, as in `'x'`. +Just like C, Rust understands a number of character escapes, using the backslash character, such as `\n`, `\r`, and `\t`. String literals, -written between double quotes, allow the same escape sequences. Rust strings -may contain newlines. +written between double quotes, allow the same escape sequences. +More on strings [later](#vectors-and-strings). + +The nil type, written `()`, has a single value, also written `()`. ## Operators From e971595cfa135dc7fe63bc01eef533bf6b7390a8 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Thu, 20 Dec 2012 13:11:23 -0800 Subject: [PATCH 18/26] tutorial: Mention rusti with other tools --- doc/tutorial.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/tutorial.md b/doc/tutorial.md index 0a50bcc45b495..b618fb1d73d57 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -115,7 +115,8 @@ for more information on them. When complete, `make install` will place several programs into `/usr/local/bin`: `rustc`, the Rust compiler; `rustdoc`, the -API-documentation tool, and `cargo`, the Rust package manager. +API-documentation tool, `cargo`, the Rust package manager, +and `rusti`, the Rust REPL. [wiki-start]: https://github.com/mozilla/rust/wiki/Note-getting-started-developing-Rust [tarball]: http://dl.rust-lang.org/dist/rust-0.5.tar.gz From 89caa319f5fb1fe88f2f295749f340ac5300615a Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Thu, 20 Dec 2012 13:16:27 -0800 Subject: [PATCH 19/26] tutorial: Update scope --- doc/tutorial.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/doc/tutorial.md b/doc/tutorial.md index b618fb1d73d57..38a9a271f9fd4 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -36,10 +36,9 @@ type system and memory model, generics, and modules. [Additional tutorials](#what-next) cover specific language features in greater depth. -This tutorial assumes that the reader is familiar with the basic concepts of -programming, and has programmed in one or more other languages -before. We will often compare Rust to other languages, -particularly those in the C family. +This tutorial assumes that the reader is already familiar with one or more +more languages in the C family. Understanding of pointers and general +memory management techniques will help. ## Conventions From 1b2af165fdc8939e71cf376d937a678d0c79d326 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Thu, 20 Dec 2012 13:18:37 -0800 Subject: [PATCH 20/26] tutorial: Fix example in syntax basics section --- doc/tutorial.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/tutorial.md b/doc/tutorial.md index 38a9a271f9fd4..7af645b2b805e 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -225,7 +225,7 @@ name. Constants, an the other hand, always require a type annotation. ~~~~ const monster_factor: float = 57.8; -let monster_size: float = monster_factor * 10.0; +let monster_size = monster_factor * 10.0; let monster_size: int = 50; ~~~~ From 51c8ba04334dd5032884aa8cfed9810b8c15646c Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Thu, 20 Dec 2012 13:20:02 -0800 Subject: [PATCH 21/26] tutorial: Editing --- doc/tutorial.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/tutorial.md b/doc/tutorial.md index 7af645b2b805e..48ac8d261d6f6 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -241,12 +241,12 @@ _monster_size = 50;`. Rust identifiers start with an alphabetic character or an underscore, and after that may contain any sequence of alphabetic characters, numbers, or underscores. The preferred style is to -begin function, variable, and module names with a lowercase letter, using +write function, variable, and module names with lowercase letters, using underscores where they help readability, while writing types in camel case. ~~~ let my_variable = 100; -type MyType = int; // some built-in types are _not_ camel case +type MyType = int; // primitive types are _not_ camel case ~~~ ## Expressions and semicolons From 28aae8976a3447c94a3c2400df9358d9ef1d6700 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Thu, 20 Dec 2012 13:21:23 -0800 Subject: [PATCH 22/26] tutorial: Remove some trivia about operators --- doc/tutorial.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/doc/tutorial.md b/doc/tutorial.md index 48ac8d261d6f6..38e6c0f107c80 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -371,11 +371,6 @@ let y: uint = x as uint; assert y == 4u; ~~~~ -The main difference with C is that `++` and `--` are missing, and that -the logical bitwise operators have higher precedence—in C, `x & 2 > 0` -means `x & (2 > 0)`, but in Rust, it means `(x & 2) > 0`, which is -more likely to be what a novice expects. - ## Syntax extensions *Syntax extensions* are special forms that are not built into the language, From a9bb1c9ef433738f71090e2017c06e2be71e058b Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Thu, 20 Dec 2012 13:42:13 -0800 Subject: [PATCH 23/26] tutorial: Try to fit the early discussion of :: in better --- doc/tutorial.md | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/doc/tutorial.md b/doc/tutorial.md index 38e6c0f107c80..74943df8d001e 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -128,7 +128,7 @@ we have a file `hello.rs` containing this program: ~~~~ fn main() { - io::println("hello?"); + core::io::println("hello?"); } ~~~~ @@ -142,8 +142,8 @@ error. If you introduce an error into the program (for example, by changing an error message like this: ~~~~ {.notrust} -hello.rs:2:4: 2:16 error: unresolved name: io::print_with_unicorns -hello.rs:2 io::print_with_unicorns("hello?"); +hello.rs:2:4: 2:16 error: unresolved name: core::io::print_with_unicorns +hello.rs:2 core::io::print_with_unicorns("hello?"); ^~~~~~~~~~~~~~~~~~~~~~~ ~~~~ @@ -180,7 +180,8 @@ JavaScript, C#, or PHP), Rust will feel familiar. Code is arranged in blocks delineated by curly braces; there are control structures for branching and looping, like the familiar `if` and `while`; function calls are written `myfunc(arg1, arg2)`; operators are written the same -and mostly have the same precedence as in C; comments are again like C. +and mostly have the same precedence as in C; comments are again like C; +module names are separated with double-colon, `::`, as with C++. The main surface difference to be aware of is that the condition at the head of control structures like `if` and `while` do not require @@ -188,12 +189,12 @@ parentheses, while their bodies *must* be wrapped in braces. Single-statement, unbraced bodies are not allowed. ~~~~ -# fn recalibrate_universe() -> bool { true } +# mod universe { fn recalibrate() -> bool { true } } fn main() { /* A simple loop */ loop { // A tricky calculation - if recalibrate_universe() { + if universe::recalibrate() { return; } } @@ -209,16 +210,11 @@ let hi = "hi"; let mut count = 0; while count < 10 { - io::println(hi); + core::io::println(fmt!("count: %?", i)); count += 1; } ~~~~ -The name of the function that prints a line of text, `io::println`, is -qualified: it refers to the function named `println` that's defined in the -module `io`. In Rust, a double colon separates parts of a -qualified name. For more details, see the section on [crates](#crates). - Although Rust can almost always infer the types of local variables, you can specify a variable's type by following it with a colon, then the type name. Constants, an the other hand, always require a type annotation. From 993b72458219b8b8d22e37aec40b111a1e46ac1c Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Thu, 20 Dec 2012 13:45:54 -0800 Subject: [PATCH 24/26] tutorial: Clean up language about syntax extensions --- doc/tutorial.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/tutorial.md b/doc/tutorial.md index 74943df8d001e..0367cabd3fa88 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -373,8 +373,8 @@ assert y == 4u; but are instead provided by the libraries. To make it clear to the reader when a name refers to a syntax extension, the names of all syntax extensions end with `!`. The standard library defines a few syntax extensions, the most -useful of which is `fmt!`, a `sprintf`-style text formatter that an early -compiler phase expands statically. +useful of which is `fmt!`, a `sprintf`-style text formatter that you will +often see in examples. `fmt!` supports most of the directives that [printf][pf] supports, but unlike printf, will give you a compile-time error when the types of the directives From 3113b73f8b3c9e4bdee9a8ceb013f9a739c55769 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Thu, 20 Dec 2012 13:50:43 -0800 Subject: [PATCH 25/26] tutorial: Typo --- doc/tutorial.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/tutorial.md b/doc/tutorial.md index 0367cabd3fa88..43345e5585f0d 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -217,7 +217,7 @@ while count < 10 { Although Rust can almost always infer the types of local variables, you can specify a variable's type by following it with a colon, then the type -name. Constants, an the other hand, always require a type annotation. +name. Constants, on the other hand, always require a type annotation. ~~~~ const monster_factor: float = 57.8; From 44130c1a5e50c81d88e2d38d8e5ae302ad288aa0 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Thu, 20 Dec 2012 13:55:52 -0800 Subject: [PATCH 26/26] tutorial: Re-remove core:: prefix from some examples --- doc/tutorial.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/tutorial.md b/doc/tutorial.md index 43345e5585f0d..c98a96c10def1 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -128,7 +128,7 @@ we have a file `hello.rs` containing this program: ~~~~ fn main() { - core::io::println("hello?"); + io::println("hello?"); } ~~~~ @@ -142,8 +142,8 @@ error. If you introduce an error into the program (for example, by changing an error message like this: ~~~~ {.notrust} -hello.rs:2:4: 2:16 error: unresolved name: core::io::print_with_unicorns -hello.rs:2 core::io::print_with_unicorns("hello?"); +hello.rs:2:4: 2:16 error: unresolved name: io::print_with_unicorns +hello.rs:2 io::print_with_unicorns("hello?"); ^~~~~~~~~~~~~~~~~~~~~~~ ~~~~ @@ -210,7 +210,7 @@ let hi = "hi"; let mut count = 0; while count < 10 { - core::io::println(fmt!("count: %?", i)); + io::println(fmt!("count: %?", i)); count += 1; } ~~~~