From 0b3dba42cf62419518137ec17ce7630f0174f26c Mon Sep 17 00:00:00 2001 From: Graydon Hoare Date: Wed, 25 Jul 2012 15:59:24 -0700 Subject: [PATCH] Change iface and interface to trait. Close #2967. --- doc/tutorial.md | 75 +++++++++++++++++++++++++------------------------ 1 file changed, 38 insertions(+), 37 deletions(-) diff --git a/doc/tutorial.md b/doc/tutorial.md index fbb1ca416e4..59e1a9d7d9d 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -34,8 +34,9 @@ high-level features include: * ***Higher-order functions.*** Rust functions may take closures as arguments or return closures as return values. Closures in Rust are very powerful and used pervasively. -* ***Interface polymorphism.*** Rust's type system features a unique - combination of Java-style interfaces and Haskell-style typeclasses. +* ***Trait polymorphism.*** Rust's type system features a unique + combination of Java-style interfaces and Haskell-style typeclasses + called _traits_. * ***Parametric polymorphism (generics).*** Functions and types can be parameterized over type variables with optional type constraints. * ***Type inference.*** Type annotations on local variable @@ -2089,9 +2090,9 @@ resource type. Rust has several kinds that can be used as type bounds: mutable fields nor shared boxes. > ***Note:*** Rust type kinds are syntactically very similar to -> [interfaces](#interfaces) when used as type bounds, and can be -> conveniently thought of as built-in interfaces. In the future type -> kinds will actually be interfaces that the compiler has special +> [traits](#traits) when used as type bounds, and can be +> conveniently thought of as built-in traits. In the future type +> kinds will actually be traits that the compiler has special > knowledge about. ## Generic functions and argument-passing @@ -2388,9 +2389,9 @@ This makes it possible to rebind a variable without actually mutating it, which is mostly useful for destructuring (which can rebind, but not assign). -# Interfaces +# Traits -Interfaces are Rust's take on value polymorphism—the thing that +Traits are Rust's take on value polymorphism—the thing that object-oriented languages tend to solve with methods and inheritance. For example, writing a function that can operate on multiple types of collections. @@ -2400,27 +2401,27 @@ collections. ## Declaration -An interface consists of a set of methods. A method is a function that +A trait consists of a set of methods. A method is a function that can be applied to a `self` value and a number of arguments, using the dot notation: `self.foo(arg1, arg2)`. -For example, we could declare the interface `to_str` for things that +For example, we could declare the trait `to_str` for things that can be converted to a string, with a single method of the same name: ~~~~ -iface to_str { +trait to_str { fn to_str() -> ~str; } ~~~~ ## Implementation -To actually implement an interface for a given type, the `impl` form +To actually implement an trait for a given type, the `impl` form is used. This defines implementations of `to_str` for the `int` and `~str` types. ~~~~ -# iface to_str { fn to_str() -> ~str; } +# trait to_str { fn to_str() -> ~str; } impl of to_str for int { fn to_str() -> ~str { int::to_str(self, 10u) } } @@ -2439,13 +2440,13 @@ method that matches the name, and simply calls that. Implementations are not globally visible. Resolving a method to an implementation requires that implementation to be in scope. You can -import and export implementations using the name of the interface they +import and export implementations using the name of the trait they implement (multiple implementations with the same name can be in scope without problems). Or you can give them an explicit name if you prefer, using this syntax: ~~~~ -# iface to_str { fn to_str() -> ~str; } +# trait to_str { fn to_str() -> ~str; } impl nil_to_str of to_str for () { fn to_str() -> ~str { ~"()" } } @@ -2461,7 +2462,7 @@ known at compile time, it is possible to specify 'bounds' for type parameters. ~~~~ -# iface to_str { fn to_str() -> ~str; } +# trait to_str { fn to_str() -> ~str; } fn comma_sep(elts: ~[T]) -> ~str { let mut result = ~"", first = true; for elts.each |elt| { @@ -2476,18 +2477,18 @@ fn comma_sep(elts: ~[T]) -> ~str { The syntax for this is similar to the syntax for specifying that a parameter type has to be copyable (which is, in principle, another kind of bound). By declaring `T` as conforming to the `to_str` -interface, it becomes possible to call methods from that interface on +trait, it becomes possible to call methods from that trait on values of that type inside the function. It will also cause a compile-time error when anyone tries to call `comma_sep` on an array whose element type does not have a `to_str` implementation in scope. -## Polymorphic interfaces +## Polymorphic traits -Interfaces may contain type parameters. This defines an interface for +Traits may contain type parameters. This defines a trait for generalized sequence types: ~~~~ -iface seq { +trait seq { fn len() -> uint; fn iter(fn(T)); } @@ -2500,25 +2501,25 @@ impl of seq for ~[T] { ~~~~ Note that the implementation has to explicitly declare the its -parameter `T` before using it to specify its interface type. This is +parameter `T` before using it to specify its trait type. This is needed because it could also, for example, specify an implementation of `seq`—the `of` clause *refers* to a type, rather than defining one. -The type parameters bound by an iface are in scope in each of the +The type parameters bound by a trait are in scope in each of the method declarations. So, re-declaring the type parameter -`T` as an explicit type parameter for `len` -- in either the iface or +`T` as an explicit type parameter for `len` -- in either the trait or the impl -- would be a compile-time error. -## The `self` type in interfaces +## The `self` type in traits -In an interface, `self` is a special type that you can think of as a -type parameter. An implementation of the interface for any given type +In a trait, `self` is a special type that you can think of as a +type parameter. An implementation of the trait for any given type `T` replaces the `self` type parameter with `T`. The following -interface describes types that support an equality operation: +trait describes types that support an equality operation: ~~~~ -iface eq { +trait eq { fn equals(&&other: self) -> bool; } @@ -2530,15 +2531,15 @@ impl of eq for int { Notice that `equals` takes an `int` argument, rather than a `self` argument, in an implementation for type `int`. -## Casting to an interface type +## Casting to an trait type The above allows us to define functions that polymorphically act on -values of *an* unknown type that conforms to a given interface. +values of *an* unknown type that conforms to a given trait. However, consider this function: ~~~~ # type circle = int; type rectangle = int; -# iface drawable { fn draw(); } +# trait drawable { fn draw(); } # impl of drawable for int { fn draw() {} } # fn new_circle() -> int { 1 } fn draw_all(shapes: ~[T]) { @@ -2549,14 +2550,14 @@ fn draw_all(shapes: ~[T]) { ~~~~ You can call that on an array of circles, or an array of squares -(assuming those have suitable `drawable` interfaces defined), but not +(assuming those have suitable `drawable` traits defined), but not on an array containing both circles and squares. -When this is needed, an interface name can be used as a type, causing +When this is needed, a trait name can be used as a type, causing the function to be written simply like this: ~~~~ -# iface drawable { fn draw(); } +# trait drawable { fn draw(); } fn draw_all(shapes: ~[drawable]) { for shapes.each |shape| { shape.draw(); } } @@ -2571,11 +2572,11 @@ is very similar to the 'vtables' used in most object-oriented languages. To construct such a value, you use the `as` operator to cast a value -to an interface type: +to a trait type: ~~~~ # type circle = int; type rectangle = int; -# iface drawable { fn draw(); } +# trait drawable { fn draw(); } # impl of drawable for int { fn draw() {} } # fn new_circle() -> int { 1 } # fn new_rectangle() -> int { 2 } @@ -2594,10 +2595,10 @@ Note that the allocation of a box is somewhat more expensive than simply using a type parameter and passing in the value as-is, and much more expensive than statically resolved method calls. -## Interface-less implementations +## Trait-less implementations If you only intend to use an implementation for static overloading, -and there is no interface available that it conforms to, you are free +and there is no trait available that it conforms to, you are free to leave off the `of` clause. However, this is only possible when you are defining an implementation in the same module as the receiver type, and the receiver type is a named type (i.e., an enum or a