From 7eae2044b0f4bbb586cd48993d9a4d1853028cc7 Mon Sep 17 00:00:00 2001 From: Tim Chevalier Date: Mon, 16 Jul 2012 10:19:39 -0700 Subject: [PATCH] Discuss classes in reference manual Still could use work, but this is the best I've got for now. --- doc/rust.md | 148 +++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 129 insertions(+), 19 deletions(-) diff --git a/doc/rust.md b/doc/rust.md index b7d5925def0..76c99f7b226 100644 --- a/doc/rust.md +++ b/doc/rust.md @@ -1106,11 +1106,12 @@ let a: list = cons(7, @cons(13, @nil)); ### Classes -TODO: more about classes - -_Classes_ are named record types that may have a destructor associated -with them, as well as fields and methods. For historical reasons, we -may call a class with a destructor and a single field a "resource". +A _class_ is a named record type that collects together fields and +methods. It must have a _constructor_ (a function called `new` that +returns a new instance of the class), and may have a destructor (a +nullary function called `drop` that executes before the memory manager +frees the memory for a given class). For historical reasons, we may +call a class with a destructor and a single field a "resource". A _class item_ declares a class type: @@ -1123,21 +1124,130 @@ class file_descriptor { ~~~~ Calling the `file_descriptor` constructor function on an integer will -produce a value with the `file_descriptor` type. Resource types have a -noncopyable [type kind](#type-kinds), and thus may not be -copied. Class types that don't have destructors may be copied if all -their fields are copyable. The semantics guarantee that for each -constructed resource value, the destructor will run once: when the -value is disposed of (barring drastic program termination that somehow -prevents unwinding from taking place). For stack-allocated values, -disposal happens when the value goes out of scope. For values in -shared boxes, it happens when the reference count of the box reaches -zero. +produce a value with the `file_descriptor` type. -The argument or arguments to the class constructor may be stored in -the class's named fields, and can be accessed by a field reference. In -this case, the `file_descriptor`'s data field would be accessed like -`f.fd`, if `f` is a value of type `file_descriptor`. +_Fields_ are immutable by default, so instances of `file_descriptor` +can't have their `fd` fields reassigned. A mutable field declaration +looks like: + +~~~~ + let mut fd: libc::c_int; +~~~~ + +The only exception is that the body of the class constructor begins +with all the class's fields uninitialized, and is allowed to -- in +fact, must -- initialize all the fields. A special case in the +typestate pass enforces this invariant. + +Usually, the class constructor stores its argument or arguments in the +class's named fields. In this case, the `file_descriptor`'s data field +would be accessed like `f.fd`, if `f` is a value of type +`file_descriptor`. By default, class fields are _public_: they can be +accessed both from methods inside the class, and code outside the +class. Classes can also have private fields: + +~~~~ +class file_descriptor { + let fd: *libc::FILE; + new(fd: *libc::FILE) { + self.fd = fd; self.name = none; + } + priv { + let mut name: option; + } + fn get_name() -> str { + alt self.name { + none { fail "File has no name!"; } + some(n) { n } + } + } +} +~~~~ + +Private fields are instance-private: methods in a class `C` can access +`self`'s private fields, but not private fields of other values of +type `C`. Code outside a class can't access any private fields. + +A class item may contain _methods_, which take an implicit `self` +argument: + +~~~~ +class file_descriptor { + let fd: *libc::FILE; + new(fd: *libc::FILE) { self.fd = fd; } + fn flush() { + libc::fflush(self.fd); + } +} +~~~~ + +In this case, ```open``` is a nullary method that calls the +```fopen``` function, defined in another library, on the ```fd``` +field. As in this example, methods must refer to their self's fields +as fields of ```self```; bare references to ```fd``` can't +occur. Methods can be public or private; just like fields, they are +public by default and private if enclosed in a `priv` section. + +Classes may be polymorphic: + +~~~~ +class file { + let data: A; + let fd: *libc::FILE; + new(data: A, fd: *libc::FILE) { self.data = data; self.fd = fd; } +} +~~~~ + +Methods may also be polymorphic, and can have additional type +parameters other than those bound in the class: + +~~~~ +class file { + let data: A; + let fd: *libc::FILE; + new(fd: *libc::FILE, data: A) { self.fd = fd; self.data = data; } + fn map_data(f: fn(A) -> B) -> B { + f(self.data) + } +} +~~~~ + +Classes do not support inheritance, except through traits. As a +result, all class method dispatch is static (non-virtual). + +A class may implement a trait (see [interfaces](#interfaces)): + +~~~~ +trait to_str { + fn to_str() -> str; +} + +class file : to_str { + let fd: *libc::FILE; + new(fd: *libc::FILE) { self.fd = fd; } + fn to_str() -> str { "a file" } +} +~~~~ + +The syntax `class file: to_str` is pronounced "class `file` +implements trait `to_str`". + +Class instances may be allocated on the stack, in the exchange heap, +or on the task heap. A value with a class type ```C``` has a +noncopyable [type kind](#type-kinds) if ```C``` has a destructor, and +thus may not be copied. Class types that don't have destructors may be +copied if all their fields are copyable. + +The semantics guarantee that for each constructed resource value, the +destructor will run once: when the value is disposed of (barring +drastic program termination that somehow prevents unwinding from +taking place). For stack-allocated values, disposal happens when the +value goes out of scope. For values in shared boxes, it happens when +the reference count of the box reaches zero. + +The order of fields in a class instance is significant; its runtime +representation is the same as that of a record with identical fields +laid out in the same order. ### Interfaces