-
Notifications
You must be signed in to change notification settings - Fork 1
Note development roadmap
This page used to cover early (pre-0.1) tasks on the development roadmap for Rust. It is now an outline of where we plan to take the compiler over the course of the next several releases, ending in a 1.0 release that we commit to supporting over an extended time period. Where possible, we will link to bugs in the issue tracker.
Please note: when we do stabilize for a 1.0 release, it will not mean we are freezing the language for all time; it means we'll be branching and supporting a stable branch and a versioning strategy that "works" for an extended period (i.e. "years"), as well as committing to providing tools to help migrate code forward, when and if breaking changes are made in future versions.
Items on this page will move to the Doc detailed release notes page as they are completed.
We may change the module-separator from ::
back to .
. There's little consensus on this at the moment.
Loops currently cannot carry labels, which makes breaking from deep within a loop difficult. There's a pretty clear way to implement this, it just requires some care to avoid clashing with nearby syntax.
Either next
, loop
, again
, or similar. Unlikely to go with continue
since all other syntax changes are converging once more on sub-5-letter keywords. Totally cosmetic change.
Macros are changing to work on uniform balanced token-trees -- much like s-expressions -- rather than the existing system which is based on expressions (with separate quoters for type, item and pattern grammars). Concurrently, we are likely to introduce the "new" macro system with a new syntax that is a bit easier on the eyes: macro_name! args
, where args is a balanced token-tree. There's still some disagreement on whether this reads better than #macro_name(...)
but we'll decide at some point and stick with one or the other.
This is a minor change that should effect no code presently; we'll be introducing a "raw string" form (possibly with a variety of legal delimiters) that does not balance the delimiter characters, so requires internal escaping of only the delimiter. This will replace the proposed (but never implemented) character-balanced custom lexeme syntax in the syntax-extension system. The cost of having a non-regular token grammar was deemed not worth the benefit, and most of the use cases for the latter are easily handled by the former.
We will likely change the region-pointer sigil from &
to ^
(still some discussion) and change to referring to them as "borrowed pointers". There is no semantic change here, just a clarification one. The sigil change is motivated by a desire to differentiate a by-reference capture in a pattern (likely to use the &
operator) from a borrowed pointer constructor in the pattern itself.
Closures have to encode their kind (whether they copy their environment, uniquely copy the unique parts, or only hold a safe reference to it). Currently this is indicated by a sigil: fn@
or fn~
or fn&
or such. We're likely to change this to one of the kind names trailing the word fn
. That is, call it fn:copy
or fn:send
.
Traits are interfaces that carry method implementations and requirements on the self-type; they can be used to achieve code reuse without introducing much of the pain that comes from conventional inheritance: they can be mixed in any order, can be declared independent from the type they affect, and are compiled independently for each type that implements them (indeed, will inline and specialize exactly as any other method will). This work will replace the iface
keyword with trait
and provide (hopefully) most of what people miss when writing rust in OO style.
Along with introducing traits for code-reuse, we will likely trim down the class
construct in the current version of rust to carry only the minimum necessary to complement traits: a nominal record type with some form of access control on its fields. This may subsume some or all of the space currently occupied by class
, record types or enums
; discussion here is still ongoing.
Currently when a user calls a method defined by an impl
, the code selected is chosen based on the impl
s that are imported into the client code's scope. This was chosen as a way to be unambiguous about selecting implementations -- a problem in any typeclass system -- but in practice it has been very confusing for users: many are unable to tell why a method can or cannot be seen due to the presence or absence of imports. It also leaves open a few "gotchas" to do with passing data values between modules with different visible imports, apparently calling the same methods or instantiating the same iface
s, but selecting different impl
s.
One way or another (there are at least 2, maybe 3 ways in discussion) we will be enforcing that only one implementation of an interface (or trait) exists per type, and removing the relationship between impl
selection and imported symbols altogether. This may happen by a per-crate static check during compile time, or it may happen by construction (making it impossible to declare implementations outside traits).
Currently we have a full "language level" construct for a value-with-a-destructor: a drop
block inside a class. This is still more machinery than strictly necessary, and we wish to transition to merely interpreting the presence of a distinguished interface (intrinsic::drop
) as indicative of a value having a destructor. All the same kind-related rules will apply, this is just a matter of removing surface machinery from the language.
This is simply removing code that has proven too difficult to predict and control in practice, and is now redundant with first class borrowed pointers.
We have tried this several times in the past to little success, but Niko believes there is a reasonably good chance that mut
will work better as a full type constructor rather than a slot-qualifier. This is one of the larger unknowns in the current roadmap.
Rust types are stratified into different "kinds": the copy
kind or the send
kind, for example. These appear in the type system as special interfaces but their existence and the conformance of a type to the interface is implemented by the compiler, by analyzing the structure of the type. For example, the compiler will not make a type with a destructor, or a function that captures its environment by reference, a member of the copy
kind, as it cannot be sensibly copied; likewise a type with @
-values in its substructure cannot be safely sent between tasks, so will not be put in the send
kind.
A new kind is appearing in 0.3, called const
, that applies to send
-kind types that additionally have no mut
fields anywhere in their substructure. These types are, for the sake of concurrent access, read only values that can be safely read by multiple threads at the same time. They're as good as values held in the read-only segment of a program.
The kind exists to support various library functions, for example a reference-counted multi-reader variable (called core::arc
) as well as, in the future, a function freeze
that can be used to convert a send
value to a const
value by casting it to a type with all mut
type modifiers removed. We expect this to be useful when implementing publish/subscribe patterns between concurrent tasks.
A very preliminary form of this should arrive in 0.3: type descriptors contain a compiler-generated function that calls visitor-methods on a predefined intrinsic visitor interface. This enables reflecting on a value without knowing its type (with some supporting library work). Much existing code will gradually shift over to this interface, as it subsumes a number of other tasks the compiler and runtime are currently doing as special cases.
There are a bunch of changes in here, all inter-related. They're mostly agreed-on though.
This just gets rid of a couple keywords and feeds into the third item. When an item is marked with extern
, if it does not have a body it is a declaration of code-written-in-C (or some other foreign language) linked to rust; if it has a body it is a declaration of code-written-in-rust that should be exposed via some foreign ABI.
This has to do with making the resolve pass coherent. In the old resolve code, resolving modules and resolving items glob-imported from modules was intermixed, and could lead to incoherence of the algorithm. In the new resolve code (landing in 0.3) there is a separation of passes: module-imports are not run through globs, only module-to-module renamings, and are resolved first, and then all imports through modules (including glob-imports) are resolved after. The module-import syntax changes to mod foo = bar;
to reflect this change.
There's some tension in readability between "ease of scanning a module's exports" and "ease of reading the code and knowing which item is exported when you're looking at it." Ultimately we came down on the side of maintainability: that it's less work for a maintainer to mark the items where they occur, rather than scrolling back and forth between export-list and item definitions. Along with moving exports to the items themselves, we'll be changing the terms to use the same keywords used for access control in classes: pub
and priv
.
Many Rust programmers stub their toes on the difference between import
and use
; both "read like" they should somehow make-available the elements in the target module. Since we are getting rid of export
(see below) there is an asymmetry in the keywords anyways, so we remove the keyword import
, switch use
to mean what import
currently means, and denote crate-linkage through extern mod foo = (...)
.
Crate files are at this point mostly an artefact of earlier beliefs that turned out not to be true (or convenient) in the compilation model. We believe they are now doing more harm than good, and their features can be presented as an "early pass" in the compilation model of a single tree of source files. Inter-file linkages will be given (within a crate) by the form mod foo = "path.rs"
.
Even with the smallest in-attribute syntax we could come up with, #[doc="..."]
, we are still finding the documentation-attribute system a little too ugly to read. We will therefore support an auxiliary form of comment that is interpreted as a doc
attribute, presented in a different form but identical in semantics. Both will remain legal but we expect most users to prefer the doc-comment form, longer-term.
The attribute system has served us well so far for conditional compilation but at times we find it not quite powerful enough. In particular, the ability to bind variables declaratively, as well as conditionally evaluate any attribute, seems lacking. We'll expand the attribute system to handle these cases, possibly changing its syntax slightly along the way.
For long-term source compatibility, we wish to make it possible (and in the case of packaged software for distribution via cargo, possibly mandatory) to tag rust source files with the language version they are written against. We'll be doing this outside the main lexical grammar, in a very simplified "pre-parse" grammar that we can expect to remain stable indefinitely. This sub-grammar is only enough to express version tags (though it uses a #
-comment form that happens to overlap with the needs of #!
at the first line of a file for running as a script).
Currently a crate has a single version, which is mangled into all the symbols in the crate as well as the crate filename. This is not quite correct. What we want is a per-item version attribute (with a per-crate default) that is mangled into each symbol, but not the output filename, such that the compiler can tolerate compiling multiple versions of the same API inside a single output file. This should be mostly invisible to users.
0.3 will introduce an API for setting and retrieving task-local data. We'll build on top of this to provide dynamic-scoped variables (keyed by global constants), on top of that, condition-handlers that can be used to recover from errors at the site of the error, or else fail. This should hopefully address many of the remaining use-cases people have in mind for catchable exceptions.
The core::io
library is due for a careful refactoring in terms of traits, condition handlers, and similar "new" abstractions that the language and libraries support. While this is true of the entire core and standard libraries, io
is particularly important in our work due to its pervasive use and numerous implementation variants.
Once core::io
is refactored, the std::serialization
library will grow several more implementations, and once we decide on a preferred backend we'll migrate the compiler metadata tables to use it. This should be reasonably unnoticed by users, but will break binary compatibility between versions when we make the change.