From ef7f833c1fa907223f80a3a4af407cfcb11b38ef Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Mon, 8 Sep 2014 12:50:27 -0700 Subject: [PATCH 001/147] Initial --- .gitignore | 1 + _config.yml | 14 ++ _includes/footer.html | 55 +++++ _includes/head.html | 11 + _includes/header.html | 27 +++ _layouts/default.html | 20 ++ _layouts/page.html | 14 ++ _layouts/post.html | 15 ++ _posts/2014-09-08-welcome-to-jekyll.markdown | 25 ++ _sass/_base.scss | 203 ++++++++++++++++ _sass/_layout.scss | 236 +++++++++++++++++++ _sass/_syntax-highlighting.scss | 67 ++++++ about.md | 11 + css/main.scss | 49 ++++ feed.xml | 30 +++ index.html | 23 ++ 16 files changed, 801 insertions(+) create mode 100644 .gitignore create mode 100644 _config.yml create mode 100644 _includes/footer.html create mode 100644 _includes/head.html create mode 100644 _includes/header.html create mode 100644 _layouts/default.html create mode 100644 _layouts/page.html create mode 100644 _layouts/post.html create mode 100644 _posts/2014-09-08-welcome-to-jekyll.markdown create mode 100644 _sass/_base.scss create mode 100644 _sass/_layout.scss create mode 100644 _sass/_syntax-highlighting.scss create mode 100644 about.md create mode 100755 css/main.scss create mode 100644 feed.xml create mode 100644 index.html diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..c08f9add7 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +_site \ No newline at end of file diff --git a/_config.yml b/_config.yml new file mode 100644 index 000000000..2f03e74d4 --- /dev/null +++ b/_config.yml @@ -0,0 +1,14 @@ +# Site settings +title: Your awesome title +email: your-email@domain.com +description: > # this means to ignore newlines until "baseurl:" + Write an awesome description for your new site here. You can edit this + line in _config.yml. It will appear in your document head meta (for + Google search results) and in your feed.xml site description. +baseurl: "" # the subpath of your site, e.g. /blog/ +url: "http://yourdomain.com" # the base hostname & protocol for your site +twitter_username: jekyllrb +github_username: jekyll + +# Build settings +markdown: kramdown diff --git a/_includes/footer.html b/_includes/footer.html new file mode 100644 index 000000000..be3976f7e --- /dev/null +++ b/_includes/footer.html @@ -0,0 +1,55 @@ + diff --git a/_includes/head.html b/_includes/head.html new file mode 100644 index 000000000..ec8f7ca5f --- /dev/null +++ b/_includes/head.html @@ -0,0 +1,11 @@ + + + + + + {% if page.title %}{{ page.title }}{% else %}{{ site.title }}{% endif %} + + + + + diff --git a/_includes/header.html b/_includes/header.html new file mode 100644 index 000000000..cfe381f75 --- /dev/null +++ b/_includes/header.html @@ -0,0 +1,27 @@ + diff --git a/_layouts/default.html b/_layouts/default.html new file mode 100644 index 000000000..e4ab96fb0 --- /dev/null +++ b/_layouts/default.html @@ -0,0 +1,20 @@ + + + + {% include head.html %} + + + + {% include header.html %} + +
+
+ {{ content }} +
+
+ + {% include footer.html %} + + + + diff --git a/_layouts/page.html b/_layouts/page.html new file mode 100644 index 000000000..74c1a1184 --- /dev/null +++ b/_layouts/page.html @@ -0,0 +1,14 @@ +--- +layout: default +--- +
+ +
+

{{ page.title }}

+
+ +
+ {{ content }} +
+ +
diff --git a/_layouts/post.html b/_layouts/post.html new file mode 100644 index 000000000..a2b4e52fe --- /dev/null +++ b/_layouts/post.html @@ -0,0 +1,15 @@ +--- +layout: default +--- +
+ +
+

{{ page.title }}

+ +
+ +
+ {{ content }} +
+ +
diff --git a/_posts/2014-09-08-welcome-to-jekyll.markdown b/_posts/2014-09-08-welcome-to-jekyll.markdown new file mode 100644 index 000000000..df6a3dc0e --- /dev/null +++ b/_posts/2014-09-08-welcome-to-jekyll.markdown @@ -0,0 +1,25 @@ +--- +layout: post +title: "Welcome to Jekyll!" +date: 2014-09-08 12:48:43 +categories: jekyll update +--- +You’ll find this post in your `_posts` directory. Go ahead and edit it and re-build the site to see your changes. You can rebuild the site in many different ways, but the most common way is to run `jekyll serve --watch`, which launches a web server and auto-regenerates your site when a file is updated. + +To add new posts, simply add a file in the `_posts` directory that follows the convention `YYYY-MM-DD-name-of-post.ext` and includes the necessary front matter. Take a look at the source for this post to get an idea about how it works. + +Jekyll also offers powerful support for code snippets: + +{% highlight ruby %} +def print_hi(name) + puts "Hi, #{name}" +end +print_hi('Tom') +#=> prints 'Hi, Tom' to STDOUT. +{% endhighlight %} + +Check out the [Jekyll docs][jekyll] for more info on how to get the most out of Jekyll. File all bugs/feature requests at [Jekyll’s GitHub repo][jekyll-gh]. If you have questions, you can ask them on [Jekyll’s dedicated Help repository][jekyll-help]. + +[jekyll]: http://jekyllrb.com +[jekyll-gh]: https://github.com/jekyll/jekyll +[jekyll-help]: https://github.com/jekyll/jekyll-help diff --git a/_sass/_base.scss b/_sass/_base.scss new file mode 100644 index 000000000..d0fa8dcd8 --- /dev/null +++ b/_sass/_base.scss @@ -0,0 +1,203 @@ +/** + * Reset some basic elements + */ +body, h1, h2, h3, h4, h5, h6, +p, blockquote, pre, hr, +dl, dd, ol, ul, figure { + margin: 0; + padding: 0; +} + + + +/** + * Basic styling + */ +body { + font-family: $base-font-family; + font-size: $base-font-size; + line-height: $base-line-height; + font-weight: 300; + color: $text-color; + background-color: $background-color; +} + + + +/** + * Set `margin-bottom` to maintain vertycal rhythm + */ +h1, h2, h3, h4, h5, h6, +p, blockquote, pre, +ul, ol, dl, figure, +%vertical-rhythm { + margin-bottom: $spacing-unit / 2; +} + + + +/** + * Images + */ +img { + max-width: 100%; + vertical-align: middle; +} + + + +/** + * Figures + */ +figure > img { + display: block; +} + +figcaption { + font-size: $small-font-size; +} + + + +/** + * Lists + */ +ul, ol { + margin-left: $spacing-unit; +} + +li { + > ul, + > ol { + margin-bottom: 0; + } +} + + + +/** + * Headings + */ +h1, h2, h3, h4, h5, h6 { + font-weight: 300; +} + + + +/** + * Links + */ +a { + color: $brand-color; + text-decoration: none; + + &:visited { + color: darken($brand-color, 15%); + } + + &:hover { + color: $text-color; + text-decoration: underline; + } +} + + + +/** + * Blockquotes + */ +blockquote { + color: $grey-color; + border-left: 4px solid $grey-color-light; + padding-left: $spacing-unit / 2; + font-size: 18px; + letter-spacing: -1px; + font-style: italic; + + > :last-child { + margin-bottom: 0; + } +} + + + +/** + * Code formatting + */ +pre, +code { + font-size: 15px; + border: 1px solid $grey-color-light; + border-radius: 3px; + background-color: #eef; +} + +code { + padding: 1px 5px; +} + +pre { + padding: 8px 12px; + overflow-x: scroll; + + > code { + border: 0; + padding-right: 0; + padding-left: 0; + } +} + + + +/** + * Wrapper + */ +.wrapper { + max-width: -webkit-calc(800px - (#{$spacing-unit} * 2)); + max-width: calc(800px - (#{$spacing-unit} * 2)); + margin-right: auto; + margin-left: auto; + padding-right: $spacing-unit; + padding-left: $spacing-unit; + @extend %clearfix; + + @include media-query($on-laptop) { + max-width: -webkit-calc(800px - (#{$spacing-unit})); + max-width: calc(800px - (#{$spacing-unit})); + padding-right: $spacing-unit / 2; + padding-left: $spacing-unit / 2; + } +} + + + +/** + * Clearfix + */ +%clearfix { + + &:after { + content: ""; + display: table; + clear: both; + } +} + + + +/** + * Icons + */ +.icon { + + > svg { + display: inline-block; + width: 16px; + height: 16px; + vertical-align: middle; + + path { + fill: $grey-color; + } + } +} diff --git a/_sass/_layout.scss b/_sass/_layout.scss new file mode 100644 index 000000000..def56f896 --- /dev/null +++ b/_sass/_layout.scss @@ -0,0 +1,236 @@ +/** + * Site header + */ +.site-header { + border-top: 5px solid $grey-color-dark; + border-bottom: 1px solid $grey-color-light; + min-height: 56px; + + // Positioning context for the mobile navigation icon + position: relative; +} + +.site-title { + font-size: 26px; + line-height: 56px; + letter-spacing: -1px; + margin-bottom: 0; + float: left; + + &, + &:visited { + color: $grey-color-dark; + } +} + +.site-nav { + float: right; + line-height: 56px; + + .menu-icon { + display: none; + } + + .page-link { + color: $text-color; + line-height: $base-line-height; + + // Gaps between nav items, but not on the first one + &:not(:first-child) { + margin-left: 20px; + } + } + + @include media-query($on-palm) { + position: absolute; + top: 9px; + right: 30px; + background-color: $background-color; + border: 1px solid $grey-color-light; + border-radius: 5px; + text-align: right; + + .menu-icon { + display: block; + float: right; + width: 36px; + height: 26px; + line-height: 0; + padding-top: 10px; + text-align: center; + + > svg { + width: 18px; + height: 15px; + + path { + fill: $grey-color-dark; + } + } + } + + .trigger { + clear: both; + display: none; + } + + &:hover .trigger { + display: block; + padding-bottom: 5px; + } + + .page-link { + display: block; + padding: 5px 10px; + } + } +} + + + +/** + * Site footer + */ +.site-footer { + border-top: 1px solid $grey-color-light; + padding: $spacing-unit 0; +} + +.footer-heading { + font-size: 18px; + margin-bottom: $spacing-unit / 2; +} + +.contact-list, +.social-media-list { + list-style: none; + margin-left: 0; +} + +.footer-col-wrapper { + font-size: 15px; + color: $grey-color; + margin-left: -$spacing-unit / 2; + @extend %clearfix; +} + +.footer-col { + float: left; + margin-bottom: $spacing-unit / 2; + padding-left: $spacing-unit / 2; +} + +.footer-col-1 { + width: -webkit-calc(35% - (#{$spacing-unit} / 2)); + width: calc(35% - (#{$spacing-unit} / 2)); +} + +.footer-col-2 { + width: -webkit-calc(20% - (#{$spacing-unit} / 2)); + width: calc(20% - (#{$spacing-unit} / 2)); +} + +.footer-col-3 { + width: -webkit-calc(45% - (#{$spacing-unit} / 2)); + width: calc(45% - (#{$spacing-unit} / 2)); +} + +@include media-query($on-laptop) { + .footer-col-1, + .footer-col-2 { + width: -webkit-calc(50% - (#{$spacing-unit} / 2)); + width: calc(50% - (#{$spacing-unit} / 2)); + } + + .footer-col-3 { + width: -webkit-calc(100% - (#{$spacing-unit} / 2)); + width: calc(100% - (#{$spacing-unit} / 2)); + } +} + +@include media-query($on-palm) { + .footer-col { + float: none; + width: -webkit-calc(100% - (#{$spacing-unit} / 2)); + width: calc(100% - (#{$spacing-unit} / 2)); + } +} + + + +/** + * Page content + */ +.page-content { + padding: $spacing-unit 0; +} + +.page-heading { + font-size: 20px; +} + +.post-list { + margin-left: 0; + list-style: none; + + > li { + margin-bottom: $spacing-unit; + } +} + +.post-meta { + font-size: $small-font-size; + color: $grey-color; +} + +.post-link { + display: block; + font-size: 24px; +} + + + +/** + * Posts + */ +.post-header { + margin-bottom: $spacing-unit; +} + +.post-title { + font-size: 42px; + letter-spacing: -1px; + line-height: 1; + + @include media-query($on-laptop) { + font-size: 36px; + } +} + +.post-content { + margin-bottom: $spacing-unit; + + h2 { + font-size: 32px; + + @include media-query($on-laptop) { + font-size: 28px; + } + } + + h3 { + font-size: 26px; + + @include media-query($on-laptop) { + font-size: 22px; + } + } + + h4 { + font-size: 20px; + + @include media-query($on-laptop) { + font-size: 18px; + } + } +} diff --git a/_sass/_syntax-highlighting.scss b/_sass/_syntax-highlighting.scss new file mode 100644 index 000000000..e36627da7 --- /dev/null +++ b/_sass/_syntax-highlighting.scss @@ -0,0 +1,67 @@ +/** + * Syntax highlighting styles + */ +.highlight { + background: #fff; + @extend %vertical-rhythm; + + .c { color: #998; font-style: italic } // Comment + .err { color: #a61717; background-color: #e3d2d2 } // Error + .k { font-weight: bold } // Keyword + .o { font-weight: bold } // Operator + .cm { color: #998; font-style: italic } // Comment.Multiline + .cp { color: #999; font-weight: bold } // Comment.Preproc + .c1 { color: #998; font-style: italic } // Comment.Single + .cs { color: #999; font-weight: bold; font-style: italic } // Comment.Special + .gd { color: #000; background-color: #fdd } // Generic.Deleted + .gd .x { color: #000; background-color: #faa } // Generic.Deleted.Specific + .ge { font-style: italic } // Generic.Emph + .gr { color: #a00 } // Generic.Error + .gh { color: #999 } // Generic.Heading + .gi { color: #000; background-color: #dfd } // Generic.Inserted + .gi .x { color: #000; background-color: #afa } // Generic.Inserted.Specific + .go { color: #888 } // Generic.Output + .gp { color: #555 } // Generic.Prompt + .gs { font-weight: bold } // Generic.Strong + .gu { color: #aaa } // Generic.Subheading + .gt { color: #a00 } // Generic.Traceback + .kc { font-weight: bold } // Keyword.Constant + .kd { font-weight: bold } // Keyword.Declaration + .kp { font-weight: bold } // Keyword.Pseudo + .kr { font-weight: bold } // Keyword.Reserved + .kt { color: #458; font-weight: bold } // Keyword.Type + .m { color: #099 } // Literal.Number + .s { color: #d14 } // Literal.String + .na { color: #008080 } // Name.Attribute + .nb { color: #0086B3 } // Name.Builtin + .nc { color: #458; font-weight: bold } // Name.Class + .no { color: #008080 } // Name.Constant + .ni { color: #800080 } // Name.Entity + .ne { color: #900; font-weight: bold } // Name.Exception + .nf { color: #900; font-weight: bold } // Name.Function + .nn { color: #555 } // Name.Namespace + .nt { color: #000080 } // Name.Tag + .nv { color: #008080 } // Name.Variable + .ow { font-weight: bold } // Operator.Word + .w { color: #bbb } // Text.Whitespace + .mf { color: #099 } // Literal.Number.Float + .mh { color: #099 } // Literal.Number.Hex + .mi { color: #099 } // Literal.Number.Integer + .mo { color: #099 } // Literal.Number.Oct + .sb { color: #d14 } // Literal.String.Backtick + .sc { color: #d14 } // Literal.String.Char + .sd { color: #d14 } // Literal.String.Doc + .s2 { color: #d14 } // Literal.String.Double + .se { color: #d14 } // Literal.String.Escape + .sh { color: #d14 } // Literal.String.Heredoc + .si { color: #d14 } // Literal.String.Interpol + .sx { color: #d14 } // Literal.String.Other + .sr { color: #009926 } // Literal.String.Regex + .s1 { color: #d14 } // Literal.String.Single + .ss { color: #990073 } // Literal.String.Symbol + .bp { color: #999 } // Name.Builtin.Pseudo + .vc { color: #008080 } // Name.Variable.Class + .vg { color: #008080 } // Name.Variable.Global + .vi { color: #008080 } // Name.Variable.Instance + .il { color: #099 } // Literal.Number.Integer.Long +} diff --git a/about.md b/about.md new file mode 100644 index 000000000..3ed64bb62 --- /dev/null +++ b/about.md @@ -0,0 +1,11 @@ +--- +layout: page +title: About +permalink: /about/ +--- + +This is the base Jekyll theme. You can find out more info about customizing your Jekyll theme, as well as basic Jekyll usage documentation at [jekyllrb.com](http://jekyllrb.com/) + +You can find the source code for the Jekyll new theme at: [github.com/jglovier/jekyll-new](https://github.com/jglovier/jekyll-new) + +You can find the source code for Jekyll at [github.com/jekyll/jekyll](https://github.com/jekyll/jekyll) diff --git a/css/main.scss b/css/main.scss new file mode 100755 index 000000000..84bb3a5bf --- /dev/null +++ b/css/main.scss @@ -0,0 +1,49 @@ +--- +# Only the main Sass file needs front matter (the dashes are enough) +--- +@charset "utf-8"; + + + +// Our variables +$base-font-family: Helvetica, Arial, sans-serif; +$base-font-size: 16px; +$small-font-size: $base-font-size * 0.875; +$base-line-height: 1.5; + +$spacing-unit: 30px; + +$text-color: #111; +$background-color: #fdfdfd; +$brand-color: #2a7ae2; + +$grey-color: #828282; +$grey-color-light: lighten($grey-color, 40%); +$grey-color-dark: darken($grey-color, 25%); + +$on-palm: 600px; +$on-laptop: 800px; + + + +// Using media queries with like this: +// @include media-query($palm) { +// .wrapper { +// padding-right: $spacing-unit / 2; +// padding-left: $spacing-unit / 2; +// } +// } +@mixin media-query($device) { + @media screen and (max-width: $device) { + @content; + } +} + + + +// Import partials from `sass_dir` (defaults to `_sass`) +@import + "base", + "layout", + "syntax-highlighting" +; diff --git a/feed.xml b/feed.xml new file mode 100644 index 000000000..022378beb --- /dev/null +++ b/feed.xml @@ -0,0 +1,30 @@ +--- +layout: null +--- + + + + {{ site.title | xml_escape }} + {{ site.description | xml_escape }} + {{ site.url }}{{ site.baseurl }}/ + + {{ site.time | date_to_rfc822 }} + {{ site.time | date_to_rfc822 }} + Jekyll v{{ jekyll.version }} + {% for post in site.posts limit:10 %} + + {{ post.title | xml_escape }} + {{ post.content | xml_escape }} + {{ post.date | date_to_rfc822 }} + {{ post.url | prepend: site.baseurl | prepend: site.url }} + {{ post.url | prepend: site.baseurl | prepend: site.url }} + {% for tag in post.tags %} + {{ tag | xml_escape }} + {% endfor %} + {% for cat in post.categories %} + {{ cat | xml_escape }} + {% endfor %} + + {% endfor %} + + diff --git a/index.html b/index.html new file mode 100644 index 000000000..83d939851 --- /dev/null +++ b/index.html @@ -0,0 +1,23 @@ +--- +layout: default +--- + +
+ +

Posts

+ + + +

subscribe via RSS

+ +
From 1cdc50afd233d7d7189ffb4e034832b2a9b73716 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Mon, 8 Sep 2014 13:02:00 -0700 Subject: [PATCH 002/147] Add the github-pages gem --- .gitignore | 1 + Gemfile | 1 + 2 files changed, 2 insertions(+) create mode 100644 Gemfile diff --git a/.gitignore b/.gitignore index c08f9add7..78bd97213 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ +.sass-cache _site \ No newline at end of file diff --git a/Gemfile b/Gemfile new file mode 100644 index 000000000..c5d8e4676 --- /dev/null +++ b/Gemfile @@ -0,0 +1 @@ +gem 'github-pages' \ No newline at end of file From 02e144ab86490ab9920da6e208f40f592254dbac Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Mon, 8 Sep 2014 13:08:45 -0700 Subject: [PATCH 003/147] Config --- _config.yml | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/_config.yml b/_config.yml index 2f03e74d4..45daabe43 100644 --- a/_config.yml +++ b/_config.yml @@ -1,14 +1,11 @@ # Site settings -title: Your awesome title -email: your-email@domain.com +title: The Rust Programming Language Blog description: > # this means to ignore newlines until "baseurl:" - Write an awesome description for your new site here. You can edit this - line in _config.yml. It will appear in your document head meta (for - Google search results) and in your feed.xml site description. + Words from the Rust team baseurl: "" # the subpath of your site, e.g. /blog/ -url: "http://yourdomain.com" # the base hostname & protocol for your site -twitter_username: jekyllrb -github_username: jekyll +url: "http://rust-lang.org" +twitter_username: rustlang +github_username: rust-lang # Build settings markdown: kramdown From 34623cf3f9bfbab82dadaad2692d9c49f895484b Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Mon, 8 Sep 2014 13:11:08 -0700 Subject: [PATCH 004/147] Fix canonical URL --- _config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_config.yml b/_config.yml index 45daabe43..4fddd8eca 100644 --- a/_config.yml +++ b/_config.yml @@ -3,7 +3,7 @@ title: The Rust Programming Language Blog description: > # this means to ignore newlines until "baseurl:" Words from the Rust team baseurl: "" # the subpath of your site, e.g. /blog/ -url: "http://rust-lang.org" +url: "http://blog.rust-lang.org" twitter_username: rustlang github_username: rust-lang From 60e8fd449da8d8f83e6a7e07452933bd6d0677a7 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Mon, 8 Sep 2014 13:23:58 -0700 Subject: [PATCH 005/147] Add CNAME --- CNAME | 1 + 1 file changed, 1 insertion(+) create mode 100644 CNAME diff --git a/CNAME b/CNAME new file mode 100644 index 000000000..a6d5f4379 --- /dev/null +++ b/CNAME @@ -0,0 +1 @@ +blog.rust-lang.org \ No newline at end of file From a8bf095455c3a2884ca921ce1bae2e0a61ca997e Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Mon, 8 Sep 2014 13:29:01 -0700 Subject: [PATCH 006/147] Bleh --- _config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/_config.yml b/_config.yml index 4fddd8eca..c9e370977 100644 --- a/_config.yml +++ b/_config.yml @@ -1,8 +1,8 @@ # Site settings title: The Rust Programming Language Blog -description: > # this means to ignore newlines until "baseurl:" +description: > Words from the Rust team -baseurl: "" # the subpath of your site, e.g. /blog/ +baseurl: "" url: "http://blog.rust-lang.org" twitter_username: rustlang github_username: rust-lang From 53c82eab08ec23f85dde88b0630e92be1b2149ec Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Mon, 8 Sep 2014 13:40:34 -0700 Subject: [PATCH 007/147] CNAME --- CNAME | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CNAME b/CNAME index a6d5f4379..b7cc76842 100644 --- a/CNAME +++ b/CNAME @@ -1 +1 @@ -blog.rust-lang.org \ No newline at end of file +blog.rust-lang.org From 420589dd0a60cec8dccc34c003b9c16ce4bcb64c Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Mon, 8 Sep 2014 15:34:25 -0700 Subject: [PATCH 008/147] Tweak --- .gitignore | 3 ++- _posts/2014-09-08-welcome-to-jekyll.markdown | 25 ------------------- _posts/2014-09-08-welcome-to-the-rust-blog.md | 6 +++++ about.md | 11 -------- 4 files changed, 8 insertions(+), 37 deletions(-) delete mode 100644 _posts/2014-09-08-welcome-to-jekyll.markdown create mode 100644 _posts/2014-09-08-welcome-to-the-rust-blog.md delete mode 100644 about.md diff --git a/.gitignore b/.gitignore index 78bd97213..6ef5f6739 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .sass-cache -_site \ No newline at end of file +_site +*~ \ No newline at end of file diff --git a/_posts/2014-09-08-welcome-to-jekyll.markdown b/_posts/2014-09-08-welcome-to-jekyll.markdown deleted file mode 100644 index df6a3dc0e..000000000 --- a/_posts/2014-09-08-welcome-to-jekyll.markdown +++ /dev/null @@ -1,25 +0,0 @@ ---- -layout: post -title: "Welcome to Jekyll!" -date: 2014-09-08 12:48:43 -categories: jekyll update ---- -You’ll find this post in your `_posts` directory. Go ahead and edit it and re-build the site to see your changes. You can rebuild the site in many different ways, but the most common way is to run `jekyll serve --watch`, which launches a web server and auto-regenerates your site when a file is updated. - -To add new posts, simply add a file in the `_posts` directory that follows the convention `YYYY-MM-DD-name-of-post.ext` and includes the necessary front matter. Take a look at the source for this post to get an idea about how it works. - -Jekyll also offers powerful support for code snippets: - -{% highlight ruby %} -def print_hi(name) - puts "Hi, #{name}" -end -print_hi('Tom') -#=> prints 'Hi, Tom' to STDOUT. -{% endhighlight %} - -Check out the [Jekyll docs][jekyll] for more info on how to get the most out of Jekyll. File all bugs/feature requests at [Jekyll’s GitHub repo][jekyll-gh]. If you have questions, you can ask them on [Jekyll’s dedicated Help repository][jekyll-help]. - -[jekyll]: http://jekyllrb.com -[jekyll-gh]: https://github.com/jekyll/jekyll -[jekyll-help]: https://github.com/jekyll/jekyll-help diff --git a/_posts/2014-09-08-welcome-to-the-rust-blog.md b/_posts/2014-09-08-welcome-to-the-rust-blog.md new file mode 100644 index 000000000..93ab59930 --- /dev/null +++ b/_posts/2014-09-08-welcome-to-the-rust-blog.md @@ -0,0 +1,6 @@ +--- +layout: post +title: "Welcome to the Rust blog" +--- + +There's nothing here yet. diff --git a/about.md b/about.md deleted file mode 100644 index 3ed64bb62..000000000 --- a/about.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -layout: page -title: About -permalink: /about/ ---- - -This is the base Jekyll theme. You can find out more info about customizing your Jekyll theme, as well as basic Jekyll usage documentation at [jekyllrb.com](http://jekyllrb.com/) - -You can find the source code for the Jekyll new theme at: [github.com/jglovier/jekyll-new](https://github.com/jglovier/jekyll-new) - -You can find the source code for Jekyll at [github.com/jekyll/jekyll](https://github.com/jekyll/jekyll) From 49e2d55d1934baed3d92444513103b3e9ca913da Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 15 Sep 2014 14:19:05 -0400 Subject: [PATCH 009/147] Initial post --- _posts/2014-09-08-welcome-to-the-rust-blog.md | 6 - _posts/2014-09-15-Rust-1.0.md | 172 ++++++++++++++++++ 2 files changed, 172 insertions(+), 6 deletions(-) delete mode 100644 _posts/2014-09-08-welcome-to-the-rust-blog.md create mode 100644 _posts/2014-09-15-Rust-1.0.md diff --git a/_posts/2014-09-08-welcome-to-the-rust-blog.md b/_posts/2014-09-08-welcome-to-the-rust-blog.md deleted file mode 100644 index 93ab59930..000000000 --- a/_posts/2014-09-08-welcome-to-the-rust-blog.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -layout: post -title: "Welcome to the Rust blog" ---- - -There's nothing here yet. diff --git a/_posts/2014-09-15-Rust-1.0.md b/_posts/2014-09-15-Rust-1.0.md new file mode 100644 index 000000000..a84a4b7b3 --- /dev/null +++ b/_posts/2014-09-15-Rust-1.0.md @@ -0,0 +1,172 @@ +--- +layout: post +title: "Road to Rust 1.0" +--- + +Rust 1.0 is on its way! We have nailed down a concrete list of +features and are hard at work on implementing them. We plan to ship +the 1.0 beta around the end of the year. If all goes well, this will +go on to become the 1.0 release after the beta period. After +1.0 is released, future 1.x releases will be backwards compatible, +meaning that existing code will continue to compile unmodified (modulo +compiler bugs, of course). + +Of course, a Rust 1.0 release means something more than "your code +will continue to compile". Basically, it means that we think the +design of Rust finally feels right. More specifically, it feels +*minimal*. The language itself is now focused on a simple core +concept, which we call ownership and borrowing (more on this +later). Leveraging ownership and borrowing, we are been able to build +up everything else that we have needed in libraries. This is very +exciting, because any library we can write, you can write too. This +really gives us confidence that Rust will not only achieve its +original goals but also go beyond and be used for all kinds of things +that we haven't even envisioned. + +### The road to Rust 1.0 + +Rust has gone through a long evolution. If you haven't looked at Rust +in a while, you may be surprised at what you see: over the last year, +we've been radically simplifying the design. As a prominent example, +Rust once featured several pointer types, indicated by various sigils: +these are gone, and only the reference types (`&T`, `&mut T`) +remain. We have also been able to consolidate and simplify a number of +other language features, such as closures, that once sported a wide +variety of options. (Some of these changes are still in progress.) + +The key to all these changes has been a focus on the core concepts of +*ownership and borrowing*. Initially, we introduced ownership as a +means of transferring data safely and efficiently between tasks, but +over time we have realized that the same mechanism allows us to move +all sorts of things out of the language and into libraries. The +resulting design is not only simpler to learn, but it is also much +"closer to the metal" than we ever thought possible before. All Rust +language constructs have a very direct mapping to machine operations, +and Rust has no required runtime or external dependencies. When used +in its own most minimal configuration, it is even possible to write an +[operating][k1] [systems][k4] [kernel][k2] in Rust. + +Throughout these changes, though, Rust has remained true to its goal +of providing the **safety** and **convenience** of modern programming +languages, while still offering the **efficiency** and **low-level +control** that C and C++ offer. Basically, if you want to get your +hands dirty with the bare metal machine, but you don't want to spend +hours tracking down segfaults and data races, Rust is the language for +you. + +If you're not already familiar with Rust, don't worry. Over the next +few months, we plan on issuing a regular series of blog posts +exploring the language. The first few will focus on different aspects +of ownership and how it can be used to achieve safe manual memory +management, concurrency, and more. After that, we'll turn to other +aspects of the Rust language and ecosystem. + +### What is left to do + +We've made great progress, but there is still a lot to do before the +release. Here is a list of the big-ticket changes we are currently +working on: + +- *Dynamically sized types:* This extension to the type system allows + us to uniformly handle types where the size is not known at compile + time, such as an array type. This enables us to support + user-designed smart pointers that contain arrays or + objects. Nicholas Cameron [recently landed][dst] a heroic commit + implementing the bulk of the work. +- *Unboxed closures:* Our new [closure design][cd] unifies closures + and object types. Much of the spec has been implemented. +- *Associated types:* We are moving our trait system to use + [associated types][at], which really help to cut down on the level + of generic annotations required to write advanced generic + libraries. Patrick Walton has done an initial implementation. +- *Where clauses:* We are adding a flexible new form of constraints + called [where clauses][wc]. Patrick Walton already landed support + for the basic syntax, and I have implemented the remaining + functionality on a branch that should be landing soon. +- *Multidispatch traits:* We are extending traits so that they + can [match on more than one type at a time][at], which opens up a lot of + new opportunities for more ergonomic APIs. I have + prototyped this work on a branch. +- *Destructors:* We are improving our destructor semantics to not + require zeroing of memory, which should improve compilation and + execution times. Felix Klock has implemented the requisite analysis + and is in the process of landing it. +- *Green threading:* We are removing support from green threading from + the standard library and moving it out into an external + package. This allows for a closer match between the Rust model and + the underlying operating system, which makes for more efficient + programs. Aaron Turon has [written the RFC][gt] and is getting + started on that work now. + +At the library level, we are currently engaged in a sweep over libstd +to decide what portions are stable and which are not. You can +[monitor the progress][stability] here. (Note though that many of the +'unstable' items are simply things whose name will be changed slightly +to conform to conventions or other minor tweaks.) + +### Cargo and the library ecosystem + +Earlier I wrote that Rust 1.0 is not so much an endpoint as it is a +starting point. This is very true. The goal for Rust 1.0 is to be an +flexible substrate for building efficient libraries -- but libraries +aren't any good if nobody can find them or they are difficult to install. + +Enter [Cargo, the Rust package manager](http://crates.io). Cargo has +been undergoing rapid development lately and is already quite +functional. By the time +1.0 is released, we plan to also have a central repository up and +wunning, meaning that it will be simple to create and distribute Rust +libraries (which we call "crates"). Oh, and of course Cargo and its +associated server are both written in Rust. + +### Release process + +Rust releases have been following a train schedule for a long time and +we don't plan on changing that. Once we start having stable releases, +however, we'll also build up a bit more infrastructure. Our plan is to +adopt the "channel" system used by many other projects such as +[Firefox](https://www.mozilla.org/en-US/firefox/channel/), +[Chrome](http://www.chromium.org/getting-involved/dev-channel), and +[Ember.js](http://emberjs.com/builds/). + +The idea is that there are three channels: Nighly, Beta, and +Release. The Nightly channel is what you use if you want the latest +and greatest: it includes unstable features and libraries that may +still change in backwards incompatible ways. Every six weeks, we cut a +new branch and call it Beta. This branch excludes all the unstable +bits, so you know that if you are using Beta or Release, your code +will continue to compile. At the same time, the existing Beta branch +is promoted to the Stable release. We expect that production users +will prefer to test on the Beta branch and ship with the Stable +branch. Testing on Beta ensures that we get some advanced notice if we +accidentally break anything you are relying on. + +With regard to the 1.0 release specifically, the plan is to release +the 1.0 beta and then follow this same process to transition to the +official 1.0 release. However, if we find a serious flaw in the +1.0 beta, we may defer and run an additional beta period or two. After +all, it's better to wait a bit longer than wind up committed to +something broken. + +### Looking forward + +In many ways, Rust 1.0 is not so much an endpoint as it is a starting +point. Naturally, we plan on continuing to develop Rust: we have a lot +of features we want to add, many of which are already in the pipeline. +But the work that's most exciting to me is not the work that will be +done by the Rust team. Rather, I expect that having a stable base will +allow the Rust community and ecosystem to grow much more rapidly than +it already has. I can't wait to see what comes out of it. + +[f]: https://github.com/rust-lang/rust/issues?q=is%3Aopen+is%3Aissue+milestone%3A1.0 +[k1]: https://github.com/charliesome/rustboot +[k2]: https://github.com/jvns/puddle +[k3]: https://github.com/pczarn/rustboot +[k4]: https://github.com/ryanra/RustOS +[stability]: http://doc.rust-lang.org/std/stability.html +[dst]: https://github.com/rust-lang/rust/commit/7932b719ec2b65acfa8c3e74aad29346d47ee992 +[cd]: https://github.com/rust-lang/rfcs/blob/master/active/0044-closures.md +[wc]: https://github.com/rust-lang/rfcs/pull/135 +[at]: https://github.com/rust-lang/rfcs/pull/195 +[gt]: https://github.com/rust-lang/rfcs/pull/230 + From 76116d7548d60b1dd58f8d0c37e12539b2b01650 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 15 Sep 2014 14:36:42 -0400 Subject: [PATCH 010/147] add author --- _posts/2014-09-15-Rust-1.0.md | 1 + 1 file changed, 1 insertion(+) diff --git a/_posts/2014-09-15-Rust-1.0.md b/_posts/2014-09-15-Rust-1.0.md index a84a4b7b3..421ab7d56 100644 --- a/_posts/2014-09-15-Rust-1.0.md +++ b/_posts/2014-09-15-Rust-1.0.md @@ -1,6 +1,7 @@ --- layout: post title: "Road to Rust 1.0" +author: Niko Matsakis --- Rust 1.0 is on its way! We have nailed down a concrete list of From 75c283e4d26e7475bc26368876d6d2576a4a7d6b Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Mon, 15 Sep 2014 12:33:02 -0700 Subject: [PATCH 011/147] Release -> Stable --- _posts/2014-09-15-Rust-1.0.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/_posts/2014-09-15-Rust-1.0.md b/_posts/2014-09-15-Rust-1.0.md index 421ab7d56..f799b198c 100644 --- a/_posts/2014-09-15-Rust-1.0.md +++ b/_posts/2014-09-15-Rust-1.0.md @@ -131,11 +131,11 @@ adopt the "channel" system used by many other projects such as [Ember.js](http://emberjs.com/builds/). The idea is that there are three channels: Nighly, Beta, and -Release. The Nightly channel is what you use if you want the latest +Stable. The Nightly channel is what you use if you want the latest and greatest: it includes unstable features and libraries that may still change in backwards incompatible ways. Every six weeks, we cut a new branch and call it Beta. This branch excludes all the unstable -bits, so you know that if you are using Beta or Release, your code +bits, so you know that if you are using Beta or Stable, your code will continue to compile. At the same time, the existing Beta branch is promoted to the Stable release. We expect that production users will prefer to test on the Beta branch and ship with the Stable From 95fa5ecc771ed18861e352298af7d0ceef6255a4 Mon Sep 17 00:00:00 2001 From: Jesse Cooke Date: Mon, 15 Sep 2014 12:43:04 -0700 Subject: [PATCH 012/147] Fix typo --- _posts/2014-09-15-Rust-1.0.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_posts/2014-09-15-Rust-1.0.md b/_posts/2014-09-15-Rust-1.0.md index f799b198c..1a7f3b2eb 100644 --- a/_posts/2014-09-15-Rust-1.0.md +++ b/_posts/2014-09-15-Rust-1.0.md @@ -116,7 +116,7 @@ Enter [Cargo, the Rust package manager](http://crates.io). Cargo has been undergoing rapid development lately and is already quite functional. By the time 1.0 is released, we plan to also have a central repository up and -wunning, meaning that it will be simple to create and distribute Rust +running, meaning that it will be simple to create and distribute Rust libraries (which we call "crates"). Oh, and of course Cargo and its associated server are both written in Rust. From 3f572c10f54499f61c218c8f1eb6001cd96d031d Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Mon, 15 Sep 2014 15:46:03 -0400 Subject: [PATCH 013/147] add RSS autodiscovery link --- _includes/head.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_includes/head.html b/_includes/head.html index ec8f7ca5f..762c4a07b 100644 --- a/_includes/head.html +++ b/_includes/head.html @@ -5,7 +5,7 @@ {% if page.title %}{{ page.title }}{% else %}{{ site.title }}{% endif %} - + From 174018b02bee3609c7bda220a7d5660228fe4b40 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Mon, 15 Sep 2014 16:13:17 -0400 Subject: [PATCH 014/147] fix two typos http://www.reddit.com/r/rust/comments/2ghkxz/road_to_rust_10/ckj6o6r --- _posts/2014-09-15-Rust-1.0.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/_posts/2014-09-15-Rust-1.0.md b/_posts/2014-09-15-Rust-1.0.md index 1a7f3b2eb..66a15bf02 100644 --- a/_posts/2014-09-15-Rust-1.0.md +++ b/_posts/2014-09-15-Rust-1.0.md @@ -17,7 +17,7 @@ will continue to compile". Basically, it means that we think the design of Rust finally feels right. More specifically, it feels *minimal*. The language itself is now focused on a simple core concept, which we call ownership and borrowing (more on this -later). Leveraging ownership and borrowing, we are been able to build +later). Leveraging ownership and borrowing, we have been able to build up everything else that we have needed in libraries. This is very exciting, because any library we can write, you can write too. This really gives us confidence that Rust will not only achieve its @@ -92,7 +92,7 @@ working on: require zeroing of memory, which should improve compilation and execution times. Felix Klock has implemented the requisite analysis and is in the process of landing it. -- *Green threading:* We are removing support from green threading from +- *Green threading:* We are removing support for green threading from the standard library and moving it out into an external package. This allows for a closer match between the Rust model and the underlying operating system, which makes for more efficient From c0ab17003a6cfdf7c2c850b1e1d9b3be1d266c5d Mon Sep 17 00:00:00 2001 From: Chris Wong Date: Tue, 16 Sep 2014 20:16:13 +1200 Subject: [PATCH 015/147] Fix a typo --- _posts/2014-09-15-Rust-1.0.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_posts/2014-09-15-Rust-1.0.md b/_posts/2014-09-15-Rust-1.0.md index 66a15bf02..711382d00 100644 --- a/_posts/2014-09-15-Rust-1.0.md +++ b/_posts/2014-09-15-Rust-1.0.md @@ -130,7 +130,7 @@ adopt the "channel" system used by many other projects such as [Chrome](http://www.chromium.org/getting-involved/dev-channel), and [Ember.js](http://emberjs.com/builds/). -The idea is that there are three channels: Nighly, Beta, and +The idea is that there are three channels: Nightly, Beta, and Stable. The Nightly channel is what you use if you want the latest and greatest: it includes unstable features and libraries that may still change in backwards incompatible ways. Every six weeks, we cut a From 8876b525e05ff88264b67357b7eb874c41a338da Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Thu, 30 Oct 2014 17:49:34 -0700 Subject: [PATCH 016/147] Blog post: Stability as a Deliverable --- _posts/2014-10-30-Stability.md | 196 +++++++++++++++++++++++++++++++++ 1 file changed, 196 insertions(+) create mode 100644 _posts/2014-10-30-Stability.md diff --git a/_posts/2014-10-30-Stability.md b/_posts/2014-10-30-Stability.md new file mode 100644 index 000000000..7c78d348f --- /dev/null +++ b/_posts/2014-10-30-Stability.md @@ -0,0 +1,196 @@ +--- +layout: post +title: "Stability as a Deliverable" +author: Niko Matsakis and Aaron Turon +--- + +The upcoming Rust 1.0 release means +[a lot](http://blog.rust-lang.org/2014/09/15/Rust-1.0.html), but most +fundamentally it is a commitment to stability, alongside our +long-running commitment to safety. + +Starting with 1.0, we will move to +a 6-week release cycle and a menu of release "channels". The stable +release channel will provide pain-free upgrades, and the nightly +channel will give early adopters access to unfinished features as we +work on them. + +### Committing to stability + +Since the early days of Rust, there have only been two things you +could count on: safety, and change. And sometimes not the first +one. In the process of developing Rust, we've encountered a lot of +dead ends, and so it's been essential to have the freedom to change +the language as needed. + +But Rust has matured, and core aspects of the language have been +steady for a long time. The design feels right. And there is a huge +amount of pent up interest in Rust, waiting for 1.0 to ship so that +there is a stable foundation to build on. + +It's important to be clear about what we mean by stable. We don't mean +that Rust will stop evolving. We will release new versions of Rust on +a regular, frequent basis, and we hope that people will upgrade just +as regularly. But for that to happen, those upgrades need to be +painless. + +To put it simply, our responsibility is to ensure that you never dread +upgrading Rust. If your code compiles on Rust stable 1.0, it should +compile with Rust stable 1.x with a minimum of hassle. + +### The plan + +We will use a variation of the train model, first introduced in web +browsers and now widely used to provide stability without stagnation: + +* New work lands directly in the master branch. + +* Each day, the last successful build from master becomes the new nightly release. + +* Every six weeks, a beta branch is created from the current state of + master, and the previous beta is promoted to be the new stable + release. + +In short, there are three release channels -- nightly, beta, and +stable -- with regular, frequent promotions from one channel to the +next. + +New features and new APIs will be flagged as unstable via feature gates +and +[stability attributes](http://doc.rust-lang.org/reference.html#stability), +respectively. +Unstable features and standard library APIs will only be available on +the nightly branch, and only if you explicitly "opt in" to the +instability. + +The beta and stable releases, on the other hand, will only include +features and APIs deemed *stable*, which represents a commitment to +avoid breaking code that uses those features or APIs. + +### The FAQ + +There are a lot of details involved in the above process, and we plan +to publish RFCs laying out the fine points. The rest of this post will +cover some of the most important details and potential worries about +this plan. + +#### What features will be stable for 1.0? + +We've done an analysis of the current Rust ecosystem to determine the +most used crates and the feature gates they depend on, and used this +data to guide our stabilization plan. The good news is that the vast +majority of what's currently being used will be stable by 1.0: + +* There are several features that are nearly finished already: struct + variants, default type parameters, tuple indexing, and slicing syntax. + +* There are two key features that need significant more work, but are + crucial for 1.0: unboxed closures and associated types. + +* Finally, there are some widely-used features with flaws that cannot + be addressed in the 1.0 timeframe: glob imports, macros, and syntax + extensions. This is where we have to make some tough decisions. + +After extensive discussion, we plan to release globs and macros as +stable at 1.0. For globs, we believe we can address problems in a +backwards-compatible way. For macros, we will likely provide an +alternative way to define macros (with better +[hygiene](http://en.wikipedia.org/wiki/Hygienic_macro)) at some later +date, and will incrementally improve the "macro rules" feature until +then. The 1.0 release will stabilize all current macro support, +including import/export. + +On the other hand, we *cannot* stabilize syntax extensions, which are +plugins with complete access to compiler internals. Stabilizing it +would effectively forever freeze the internals of the compiler; we +need to design a more deliberate interface between extensions and the +compiler. So syntax extensions will remain behind a feature gate for +1.0. + +Many major uses of syntax extensions could be replaced with +traditional code generation, and the Cargo tool will soon be growing +specific support for this use case. We plan to work with library +authors to help them migrate away from syntax extensions prior to +1.0. Because many syntax extensions don't fit this model, we also see +stabilizing syntax extensions as an immediate priority after the 1.0 +release. + +#### What parts of the standard library will be stable for 1.0? + +We have been steadily stabilizing the standard library, and have a +plan for nearly *all* of the modules it provides. The expectation is +that the vast majority of functionality in the standard library will +be stable for 1.0. We have also been migrating more experimental APIs +out of the standard library and into their own crates. + +#### What about stability attributes outside of the standard library? + +Library authors can continue to use stability attributes as they do +today to mark their own stability promises. These attributes are not +tied into the Rust release channels by default. That is, when you're +compiling on Rust stable, you can only use stable APIs from the +standard library, but you can opt into experimental APIs from other +libraries. The Rust release channels are about making upgrading *Rust +itself* (the compiler and standard library) painless. + +Library authors should follow [semver](http://semver.org/); we will +soon publish an RFC defining how library stability attributes and +semver interact. + +#### Why not allow opting in to instability in the stable release? + +There are three problems with allowing unstable features on the +stable release. + +First, as the web has shown numerous times, merely *advertising* +instability doesn't work. Once features are in wide use it is very +hard to change them -- and once features are available at all, it is +very hard to prevent them from being used. Mechanisms like "vendor +prefixes" on the web that were meant to support experimentation +instead led to de facto standardization. + +Second, unstable features are by definition work in progress. But the +beta/stable snapshots freeze the feature at scheduled points in time, +while library authors will want to work with the latest version of the +feature. + +Finally, we simply *cannot* deliver stability for Rust unless we +enforce it. Our promise is that, if you are using the stable release +of Rust, you will never dread upgrading to the next release. If +libraries could opt in to instability, then we could only keep this +promise if all library authors guaranteed the same thing by supporting +all three release channels simultaneously. + +It's not realistic or necessary for the entire ecosystem to flawlessly +deal with these problems. Instead, we will enforce that stable means +stable: the stable channel provides only stable features. + +#### Won't this split the ecosystem? Will everyone use nightly at 1.0? + +It doesn't split the ecosystem: it creates a subset. Programmers +working with the nightly release channel can freely use libraries that +are designed for the stable channel. There will be pressure to +stabilize important features and APIs, and so the incentives to stay +in the unstable world will shrink over time. + +We have carefully planned the 1.0 release so that the bulk of the +existing ecosystem will fit into the "stable" category, and thus +newcomers to Rust will immediately be able to use most libraries on +the stable 1.0 release. + +#### What are the stability caveats? + +We reserve the right to fix compiler bugs, patch safety holes, and +change type inference in ways that may occasionally require new type +annotations. We do not expect any of these changes to cause +headaches when upgrading Rust. + +The library API caveats will be laid out in a forthcoming RFC, but are +similarly designed to minimize upgrade pain in practice. + +#### Will Rust and its ecosystem continue their rapid development? + +Yes! Because new work can land on master at any time, the train model +doesn't slow down the pace of development or introduce artificial +delays. Rust has always evolved at a rapid pace, with lots of help +from amazing community members, and we expect this will only accelerate. From d6acbc693e4aa7d44daa8aecad3fe731e6a3deae Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Thu, 30 Oct 2014 17:55:46 -0700 Subject: [PATCH 017/147] Tweaks to stability post --- _posts/2014-10-30-Stability.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/_posts/2014-10-30-Stability.md b/_posts/2014-10-30-Stability.md index 7c78d348f..19b6a225a 100644 --- a/_posts/2014-10-30-Stability.md +++ b/_posts/2014-10-30-Stability.md @@ -1,7 +1,7 @@ --- layout: post title: "Stability as a Deliverable" -author: Niko Matsakis and Aaron Turon +author: Aaron Turon and Niko Matsakis --- The upcoming Rust 1.0 release means @@ -74,7 +74,7 @@ to publish RFCs laying out the fine points. The rest of this post will cover some of the most important details and potential worries about this plan. -#### What features will be stable for 1.0? +### What features will be stable for 1.0? We've done an analysis of the current Rust ecosystem to determine the most used crates and the feature gates they depend on, and used this @@ -115,7 +115,7 @@ authors to help them migrate away from syntax extensions prior to stabilizing syntax extensions as an immediate priority after the 1.0 release. -#### What parts of the standard library will be stable for 1.0? +### What parts of the standard library will be stable for 1.0? We have been steadily stabilizing the standard library, and have a plan for nearly *all* of the modules it provides. The expectation is @@ -123,7 +123,7 @@ that the vast majority of functionality in the standard library will be stable for 1.0. We have also been migrating more experimental APIs out of the standard library and into their own crates. -#### What about stability attributes outside of the standard library? +### What about stability attributes outside of the standard library? Library authors can continue to use stability attributes as they do today to mark their own stability promises. These attributes are not @@ -137,7 +137,7 @@ Library authors should follow [semver](http://semver.org/); we will soon publish an RFC defining how library stability attributes and semver interact. -#### Why not allow opting in to instability in the stable release? +### Why not allow opting in to instability in the stable release? There are three problems with allowing unstable features on the stable release. @@ -165,7 +165,7 @@ It's not realistic or necessary for the entire ecosystem to flawlessly deal with these problems. Instead, we will enforce that stable means stable: the stable channel provides only stable features. -#### Won't this split the ecosystem? Will everyone use nightly at 1.0? +### Won't this split the ecosystem? Will everyone use nightly at 1.0? It doesn't split the ecosystem: it creates a subset. Programmers working with the nightly release channel can freely use libraries that @@ -178,7 +178,7 @@ existing ecosystem will fit into the "stable" category, and thus newcomers to Rust will immediately be able to use most libraries on the stable 1.0 release. -#### What are the stability caveats? +### What are the stability caveats? We reserve the right to fix compiler bugs, patch safety holes, and change type inference in ways that may occasionally require new type @@ -188,7 +188,7 @@ headaches when upgrading Rust. The library API caveats will be laid out in a forthcoming RFC, but are similarly designed to minimize upgrade pain in practice. -#### Will Rust and its ecosystem continue their rapid development? +### Will Rust and its ecosystem continue their rapid development? Yes! Because new work can land on master at any time, the train model doesn't slow down the pace of development or introduce artificial From a8596a4bf5ab83bf079ec6236c88e0067edbcc24 Mon Sep 17 00:00:00 2001 From: Paolo Moretti Date: Fri, 31 Oct 2014 14:40:28 +0000 Subject: [PATCH 018/147] Fix a broken link --- _posts/2014-09-15-Rust-1.0.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_posts/2014-09-15-Rust-1.0.md b/_posts/2014-09-15-Rust-1.0.md index 711382d00..3c8e06616 100644 --- a/_posts/2014-09-15-Rust-1.0.md +++ b/_posts/2014-09-15-Rust-1.0.md @@ -166,7 +166,7 @@ it already has. I can't wait to see what comes out of it. [k4]: https://github.com/ryanra/RustOS [stability]: http://doc.rust-lang.org/std/stability.html [dst]: https://github.com/rust-lang/rust/commit/7932b719ec2b65acfa8c3e74aad29346d47ee992 -[cd]: https://github.com/rust-lang/rfcs/blob/master/active/0044-closures.md +[cd]: https://github.com/rust-lang/rfcs/blob/master/text/0114-closures.md [wc]: https://github.com/rust-lang/rfcs/pull/135 [at]: https://github.com/rust-lang/rfcs/pull/195 [gt]: https://github.com/rust-lang/rfcs/pull/230 From 6628d471258d8d814794ce67ff4437fd38f28232 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Sat, 1 Nov 2014 10:05:38 +1100 Subject: [PATCH 019/147] Add the Rust logo to the site heading. --- _config.yml | 1 + _includes/header.html | 2 +- _sass/_layout.scss | 6 ++++++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/_config.yml b/_config.yml index c9e370977..91a26e990 100644 --- a/_config.yml +++ b/_config.yml @@ -4,6 +4,7 @@ description: > Words from the Rust team baseurl: "" url: "http://blog.rust-lang.org" +logo: "http://www.rust-lang.org/logos/rust-logo-64x64-blk.png" twitter_username: rustlang github_username: rust-lang diff --git a/_includes/header.html b/_includes/header.html index cfe381f75..de7fad667 100644 --- a/_includes/header.html +++ b/_includes/header.html @@ -2,7 +2,7 @@ From 231aeb2d0e2077e9397d37db8e33d7eba1ebd6be Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Sat, 1 Nov 2014 11:09:08 +1100 Subject: [PATCH 021/147] Add the site title to the page title always. --- _includes/head.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_includes/head.html b/_includes/head.html index 762c4a07b..f056adcec 100644 --- a/_includes/head.html +++ b/_includes/head.html @@ -3,7 +3,7 @@ - {% if page.title %}{{ page.title }}{% else %}{{ site.title }}{% endif %} + {% if page.title %}{{ page.title }} - {{ site.title }}{% else %}{{ site.title }}{% endif %} From d9d1b67c9b1681bf85f15513677bd08245b16db0 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Sat, 1 Nov 2014 11:10:29 +1100 Subject: [PATCH 022/147] Add links to rust-lang.org and the guide. Hopefully people will click on these to start their Rust journey! --- _includes/footer.html | 10 +++------- _sass/_layout.scss | 8 +++++++- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/_includes/footer.html b/_includes/footer.html index be3976f7e..f2d236e92 100644 --- a/_includes/footer.html +++ b/_includes/footer.html @@ -1,15 +1,11 @@ + +
- - -
+ + From 2a86f1db73b1c4737f9488633f158da182c07884 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 9 Jan 2015 11:19:24 -0800 Subject: [PATCH 041/147] Update analytics tracking number --- _includes/footer.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_includes/footer.html b/_includes/footer.html index 6370c4df6..9e267faee 100644 --- a/_includes/footer.html +++ b/_includes/footer.html @@ -56,7 +56,7 @@ m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - ga('create', 'UA-58390457-1', 'auto'); + ga('create', 'UA-58390457-2', 'auto'); ga('send', 'pageview'); From 382823b01273b6f3f0c675e7dd75f37f44f70e88 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Tue, 13 Jan 2015 00:31:50 +1100 Subject: [PATCH 042/147] Add some xml_escape's to twitter cards. Quotes etc. in titles or descriptions will make HTML & twitter unhappy without these. --- _includes/head.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/_includes/head.html b/_includes/head.html index 2aceb33cd..8dba01e36 100644 --- a/_includes/head.html +++ b/_includes/head.html @@ -13,9 +13,9 @@ {% if page.description %} - + {% else %} - + {% endif %} From 32b3922fb2b1e21e411d53650906e4596ffabe79 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Mon, 12 Jan 2015 11:40:50 -0500 Subject: [PATCH 043/147] Add @dylanede to 1.0 alpha contributors list https://github.com/rust-lang/rust/commit/25eada15740fbe12ee2cae7fc6fe8e2c228b699d was in alpha, but Dylan wasn't credited. --- _posts/2015-01-09-Rust-1.0-alpha.md | 1 + 1 file changed, 1 insertion(+) diff --git a/_posts/2015-01-09-Rust-1.0-alpha.md b/_posts/2015-01-09-Rust-1.0-alpha.md index ea0c496dc..425663a63 100644 --- a/_posts/2015-01-09-Rust-1.0-alpha.md +++ b/_posts/2015-01-09-Rust-1.0-alpha.md @@ -176,6 +176,7 @@ This alpha release could not have happened without the help of Rust's enthusiast * `Davis Silverman ` * `Diego Giagio ` * `Dirk Gadsden ` +* `Dylan Ede ` * `Earl St Sauver ` * `Eduard Burtescu ` * `Eduardo Bautista ` From 3cead0e4c4b7eee43c4c84a6d3a1bda665f7e561 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Tue, 13 Jan 2015 11:02:46 -0500 Subject: [PATCH 044/147] Update @rohitjoshi's email --- _posts/2015-01-09-Rust-1.0-alpha.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_posts/2015-01-09-Rust-1.0-alpha.md b/_posts/2015-01-09-Rust-1.0-alpha.md index 425663a63..d89b7db91 100644 --- a/_posts/2015-01-09-Rust-1.0-alpha.md +++ b/_posts/2015-01-09-Rust-1.0-alpha.md @@ -312,7 +312,7 @@ This alpha release could not have happened without the help of Rust's enthusiast * `rjz ` * `Robin Gloster ` * `Robin Stocker ` -* `Rohit Joshi ` +* `Rohit Joshi ` * `Rolf Timmermans ` * `Rolf van de Krol ` * `Roy Crihfield ` From 69c3cdf5b1b2caf79f10fa897aeb1e46f3feda7e Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Fri, 13 Feb 2015 11:16:06 -0800 Subject: [PATCH 045/147] Blog post: Rust 1.0: status report and final timeline --- _posts/2015-02-13-Final-1.0-timeline.md | 168 ++++++++++++++++++++++++ 1 file changed, 168 insertions(+) create mode 100644 _posts/2015-02-13-Final-1.0-timeline.md diff --git a/_posts/2015-02-13-Final-1.0-timeline.md b/_posts/2015-02-13-Final-1.0-timeline.md new file mode 100644 index 000000000..70cbaba12 --- /dev/null +++ b/_posts/2015-02-13-Final-1.0-timeline.md @@ -0,0 +1,168 @@ +--- +layout: post +title: "Rust 1.0: status report and final timeline" +author: The Rust Core Team +--- + +It's been five weeks since we released Rust 1.0-alpha! Before this +release cycle finishes next week, we want to give a status report and +update on the road to 1.0 final. + +**TL;DR: Rust 1.0 final will be released by May 15, 2015** + +## What is the overall timeline? + +Based on the progress in this release cycle, we are now comfortable +committing to a precise release schedule for 1.0: + +* Rust 1.0-alpha2 -- Feb 20 +* All 1.0 modules stable on nightly -- around Mar 9 +* Rust 1.0-beta -- Mar 31 +* Rust 1.0 -- May 15 + +This schedule differs from the +[previous one](http://blog.rust-lang.org/2014/12/12/1.0-Timeline.html) +by nailing down an exact set of release cycles. It also opts for a +second alpha release and only a single beta release. + +The main reason for calling the next release alpha2 rather than beta1 +is that new path and IO APIs have only recently landed, and we would +like more time to collect feedback before marking the new path and IO +APIs stable. More details are below. + +## What's shipping in alpha2? + +We've managed to land almost all of the features +[previously expected](http://blog.rust-lang.org/2015/01/09/Rust-1.0-alpha.html) +for this cycle. + +**The big headline here is that all major API revisions are +finished**: path and IO reform have landed. At this point, all modules +shipping for 1.0 are in what we expect to be their final form, modulo +minor tweaks during the alpha2 cycle. + +Other highlights are as follows: + +* **Closures**: Rust now supports + [full capture-clause inference](https://github.com/rust-lang/rfcs/blob/master/text/0231-upvar-capture-inference.md) + and has deprecated the temporary `|:|` notation, making closures + much more ergonomic to use. + +* **Destructors**: New + [destructor rules](https://github.com/rust-lang/rfcs/pull/769) + landed, obviating the need for `#[unsafe destructor]`. + +* **Path reform**: The `path` module has been completely + [redesigned](https://github.com/rust-lang/rfcs/pull/474) to resolve + a number of semantic and ergonomic problems with the old module, and + to take advantage of DST. + +* **IO reform**: The `io` system has been + [thoroughly revised](https://github.com/rust-lang/rfcs/blob/master/text/0517-io-os-reform.md) + to improve robustness and cross-platform behavior, and to eschew + ambitious high-level abstractions over the system. While almost all + of the APIs are effected by this change, the changes move toward a + much more conservative and consistent design. + +* **Deref coercions**: A + [new coercion](https://github.com/rust-lang/rfcs/pull/241) will + follow smart pointers, so that you can pass `&Vec` where `&[T]` + is wanted, or `&Arc` where `&T` is wanted. This removes most need + for explicit slicing or the dreaded "cross-borrowing" `&*`, and + means that `&` can be thought of as a general "borrow" operator. + +* **Feature staging**: Rust now has a notion of + [named API *features*](https://github.com/rust-lang/rfcs/pull/475) + akin to language features, which is how we will manage API + stabilization going forward. These named features make it easier to + manage progress in `std`, and make it plausible to detect the + minimum version of Rust needed for a crate. + +* **For loops**: The new `IntoIterator` trait is now available and + used for `for` loops, making it possible to write `for x in &vec` + rather than `for x in vec.iter()`. + +* **Range notation**: We have + [finalized range notation](https://github.com/rust-lang/rfcs/pull/702), + introducing `..` for "full ranges", which will make APIs like + `collection.remove(..)` possible in the future. + +* **Trait system**: New coherence rules were + [finalized](http://internals.rust-lang.org/t/orphan-rules/1322), + providing both flexibility and soundness for trait implementations. + +* **Overflow semantics**: After a long debate, the final integer + overflow semantics has + [been decided](https://github.com/rust-lang/rfcs/pull/560) and is + expected to land for alpha2. This change is expected to make it much + easier to find over/underflow bugs when used in conjunction with + fuzzing, etc. + +* **Associated types**: many compiler bugs around associated types + have been fixed, making them usable at large scale. + +Some other changes have not landed at the time of writing but are +expected for alpha2: +[variance for type parameters](https://github.com/rust-lang/rfcs/pull/738), +[Send changes](https://github.com/rust-lang/rfcs/pull/458), and +[the great integer audit](https://github.com/rust-lang/rust/issues/22240). + +Complete details will be available in the release notes next week. + +## Why another alpha? + +The main reason is that we want to leave recently-landed APIs, like IO +and path, unstable for a few more weeks while we collect feedback -- +but the beta release is intended to disallow use of unstable features. + +In more detail, Rust is drawing a +[difference between alpha and beta](http://blog.rust-lang.org/2014/12/12/1.0-Timeline.html) +connected with our +[stability system](http://blog.rust-lang.org/2014/10/30/Stability.html). +In alpha releases, it's possible to opt-in to unstable features, but +after beta, this will be possible only when using nightly builds. The +beta release will mark the point when a substantial portion of the +community can move off of nightlies. + +As mentioned above, we have landed all of the major API revisions +needed for the 1.0 release, including path and IO reform. However, +some of these revisions landed relatively late in the cycle, and as a +community we don't have enough experience with the revised APIs to +declare them stable yet. Note that the API changes are, with a +couple exceptions, very conservative: they generally move us in the +direction of existing, successful libraries. + +By producing 1.0-alpha2, we leave open a longer window for tweaks to +these APIs before declaring them stable. That window will close around +March 9. + +### Is there risk of slippage by not moving to beta now? + +It seems unlikely. Essentially all of the language and library +features needed for 1.0 have already landed, meaning that we will have +*12 weeks* of time to polish between alpha2 and 1.0 final. + +## What will happen before 1.0? + +All features that are required for shipping 1.0 have now landed. What +remains is polish, performance improvements, bugfixing, documentation +-- and gaining enough confidence in recently revised APIs to mark them +`#[stable]`. + +The alpha2 release will officially deprecate (but leave available) the +old path and IO APIs. The new APIs are scheduled to be stabilized +by March 9. **Please try out these new APIs and help uncover +problems!** + +After the March 9 deadline, it should be possible for substantial +crates to work with "stable Rust", i.e. without any use of +`#[feature]`. Between then and the beta release, we hope to work +directly with authors of crates.io packages to help move code to +stable Rust, and to uncover any gaps in stabilization. + +By beta, we hope that a substantial part of the ecosystem will be off +of nightlies and on to stable releases. Getting there will require a +community-wide push toward stabilization, which we're coordinating via +[discuss](http://users.rust-lang.org/t/using-unstable-apis-tell-us-about-it/157/26) +-- if you haven't, please drop by and tell us the key unstable APIs +you're using. From 5991d72ffe253f5950238a019f3f4d3dfcb7b993 Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Fri, 13 Feb 2015 11:35:52 -0800 Subject: [PATCH 046/147] Fix typos --- _posts/2015-02-13-Final-1.0-timeline.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_posts/2015-02-13-Final-1.0-timeline.md b/_posts/2015-02-13-Final-1.0-timeline.md index 70cbaba12..b7341d99b 100644 --- a/_posts/2015-02-13-Final-1.0-timeline.md +++ b/_posts/2015-02-13-Final-1.0-timeline.md @@ -61,7 +61,7 @@ Other highlights are as follows: [thoroughly revised](https://github.com/rust-lang/rfcs/blob/master/text/0517-io-os-reform.md) to improve robustness and cross-platform behavior, and to eschew ambitious high-level abstractions over the system. While almost all - of the APIs are effected by this change, the changes move toward a + of the APIs are affected by this change, the changes move toward a much more conservative and consistent design. * **Deref coercions**: A From 404f85b1d4d3496b7b0e72b587366997c42495b8 Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Fri, 13 Feb 2015 11:39:02 -0800 Subject: [PATCH 047/147] Fix redundancy --- _posts/2015-02-13-Final-1.0-timeline.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/_posts/2015-02-13-Final-1.0-timeline.md b/_posts/2015-02-13-Final-1.0-timeline.md index b7341d99b..c7db38c59 100644 --- a/_posts/2015-02-13-Final-1.0-timeline.md +++ b/_posts/2015-02-13-Final-1.0-timeline.md @@ -27,8 +27,8 @@ second alpha release and only a single beta release. The main reason for calling the next release alpha2 rather than beta1 is that new path and IO APIs have only recently landed, and we would -like more time to collect feedback before marking the new path and IO -APIs stable. More details are below. +like more time to collect feedback before marking them stable. More +details are below. ## What's shipping in alpha2? From 1565d2c25e3adb3c182d6e85988a8d0436660675 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Fri, 20 Feb 2015 14:57:43 -0500 Subject: [PATCH 048/147] Rust 1.0-alpha2 --- _posts/2015-02-20-Rust-1.0-alpha2.md | 244 +++++++++++++++++++++++++++ 1 file changed, 244 insertions(+) create mode 100644 _posts/2015-02-20-Rust-1.0-alpha2.md diff --git a/_posts/2015-02-20-Rust-1.0-alpha2.md b/_posts/2015-02-20-Rust-1.0-alpha2.md new file mode 100644 index 000000000..28bc33490 --- /dev/null +++ b/_posts/2015-02-20-Rust-1.0-alpha2.md @@ -0,0 +1,244 @@ +--- +layout: post +title: "Announcing Rust 1.0-alpha2" +author: Steve Klabnik +description: "Rust 1.0-alpha2 has been released." +--- + +Today, we are happy to announce the release of Rust 1.0.0.alpha.2! Rust is a +systems programming language perusing the trifecta: safe, fast, and concurrent. + +In accordance with our [status report](/2015/02/13/Final-1.0-timeline.html) +last week, this is a second alpha release, rather than a first beta release. +The beta release will be six weeks from today, with the final coming six weeks +after that. + +We’ve managed to land almost all of the features previously expected for this +cycle. **The big headline here is that all major API revisions are finished**: +path and IO reform have landed. At this point, all modules shipping for 1.0 are +in what we expect to be their final form, modulo minor tweaks during the alpha2 +cycle. See the [previous post](/2015/02/13/Final-1.0-timeline.html) for more +details. + +This coming release cycle is crucial to Rust, as this will be the last cycle +that we recommend nightly builds for most users. Part of the way through the +cycle, around March 9th, we expect to have all major functionality we expect in +1.0 marked as stable; we will fill in stability gaps between then and beta on +March 31st. The beta release will fully introduce the "stable channel", which +will not allow use of unstable features but will guarantee +backwards-compatibility (after 1.0). Unstable features will only be available +in nightly. + +For more detail, please see the [Release +notes](https://github.com/rust-lang/rust/blob/master/RELEASES.md#version-100-alpha2-february-2015). + +Thank you to all 207 contributors for this release: + +* `Aaron Turon ` +* `Adam Roben ` +* `Adolfo Ochagavía ` +* `Ahmed Charles ` +* `Aidan Hobson Sayers ` +* `Akos Kiss ` +* `Alexander Bliskovsky ` +* `Alexander Korolkov ` +* `Alexander Light ` +* `Alex Crichton ` +* `Alexis ` +* `Alfie John ` +* `Alfie John ` +* `Andrea Canciani ` +* `Andrew Barchuk ` +* `Andrew Paseltiner ` +* `Ariel Ben-Yehuda ` +* `Ariel Ben-Yehuda ` +* `Armin Preiml ` +* `Artem ` +* `Barosl Lee ` +* `Benjamin Peterson ` +* `Ben S ` +* `Björn Steinbrink ` +* `blackbeam ` +* `bombless ` +* `Brian Anderson ` +* `Brian Leibig ` +* `caipre ` +* `Cam Jackson ` +* `Carl Lerche ` +* `Carol Nichols ` +* `Carter Hinsley ` +* `CarVac ` +* `Caspar Krieger ` +* `Chase Southwood ` +* `Chris Morgan ` +* `Chris Thorn ` +* `Chris Wong ` +* `Clifford Caoile ` +* `Corey Farwell ` +* `Corey Richardson ` +* `Daniel Griffen ` +* `Daniel Grunwald ` +* `Daniel Raloff ` +* `Daniil Smirnov ` +* `Dan Yang ` +* `David Creswick ` +* `Diggory Blake ` +* `Dominik Inführ ` +* `Duane Edwards ` +* `Duncan Regan ` +* `Dzmitry Malyshau ` +* `Earl St Sauver ` +* `Eduard Burtescu ` +* `Edward Wang ` +* `Elantsev Serj ` +* `emanueLczirai ` +* `Erick Rivas ` +* `Erick Tryzelaar ` +* `Eunji Jeong ` +* `Felix S. Klock II ` +* `Fenhl ` +* `Filip SzczepaÅ„ski ` +* `Flavio Percoco ` +* `Florian Hahn ` +* `Garrett Heel ` +* `Geoffrey Thomas ` +* `Greg Chapple ` +* `Guillaume Gomez ` +* `GuillaumeGomez ` +* `Guillaume Pinot ` +* `Henrik Schopmans ` +* `Hugo van der Wijst ` +* `Huon Wilson ` +* `Ignacio Corderi ` +* `Ingo Blechschmidt ` +* `Jake Goulding ` +* `James Miller ` +* `Jared Roesch ` +* `Jason Fager ` +* `jatinn ` +* `Jay True ` +* `Jeff Belgum ` +* `John Hodge ` +* `John KaÌŠre Alsaker ` +* `John KÃ¥re Alsaker ` +* `Jonathan Reem ` +* `JONNALAGADDA Srinivas ` +* `Jorge Aparicio ` +* `Jorge Israel Peña ` +* `Jormundir ` +* `Joseph Crail ` +* `JP Sugarbroad ` +* `Julian Orth ` +* `Junseok Lee ` +* `Kang Seonghoon ` +* `Keegan McAllister ` +* `Keegan McAllister ` +* `Ken Tossell ` +* `KernelJ ` +* `Kevin Ballard ` +* `Kevin Butler ` +* `Kevin Yap ` +* `Kim Røen ` +* `klutzy ` +* `Kostas Karachalios ` +* `kud1ing ` +* `Lai Jiangshan ` +* `Lauri Lehmijoki ` +* `Leo Testard ` +* `Liigo Zhuang ` +* `Logan Chien ` +* `Loïc Damien ` +* `Luca Bruno ` +* `Luke Francl ` +* `Luke Steensen ` +* `madmalik ` +* `Manish Goregaokar ` +* `Markus Siemens ` +* `Marvin Löbel ` +* `Matt Roche ` +* `Mátyás Mustoha ` +* `mdinger ` +* `Michael Budde ` +* `Michael Neumann ` +* `Michael Pankov ` +* `Michael Sproul ` +* `Michael Woerister ` +* `Mike English ` +* `Mikhail Zabaluev ` +* `Ms2ger ` +* `NAKASHIMA, Makoto ` +* `nathan dotz ` +* `Nathaniel Theis ` +* `Nathan Stoddard ` +* `Nelson Chen ` +* `Nick Cameron ` +* `Nick Howell ` +* `Nick Sarten ` +* `Niko Matsakis ` +* `NODA, Kai ` +* `Oliver 'ker' Schneider ` +* `Oliver Schneider ` +* `Orpheus Lummis ` +* `P1start ` +* `Pascal Hertleif ` +* `Paul Collier ` +* `Paul Crowley ` +* `Peter Atashian ` +* `Peter Schuller ` +* `Pierre Baillet ` +* `Piotr Czarnecki ` +* `posixphreak ` +* `Potpourri ` +* `Pyfisch ` +* `Raul Gutierrez S ` +* `Renato Alves ` +* `Renato Zannon ` +* `Richo Healey ` +* `Robin Stocker ` +* `Rohit Joshi ` +* `Ryan Levick ` +* `Sean Collins ` +* `Sean Gillespie ` +* `Sean Patrick Santos ` +* `Sean T Allen ` +* `Sebastian Gesemann ` +* `Sebastian Rasmussen ` +* `Sébastien Marie ` +* `Seo Sanghyeon ` +* `Seth Faxon ` +* `Simonas Kazlauskas ` +* `Stepan Koltsov ` +* `Steve Klabnik ` +* `Steven Allen ` +* `Steven Crockett ` +* `Steven Fackler ` +* `Strahinja Val Markovic ` +* `Thiago Carvalho ` +* `Tim Brooks ` +* `Tim Cuthbertson ` +* `Tim Dumol ` +* `Tim Parenti ` +* `Tobias Bucher ` +* `Toby Scrace ` +* `Tom Chittenden ` +* `Tom Jakubowski ` +* `Toni Cárdenas ` +* `Travis Watkins ` +* `Tristan Storch ` +* `Tshepang Lekhonkhobe ` +* `Tyler Thrailkill ` +* `Ulrik Sverdrup ` +* `Vadim Chugunov ` +* `Vadim Petrochenkov ` +* `Valerii Hiora ` +* `Victory ` +* `visualfc ` +* `Vojtech Kral ` +* `Volker Mische ` +* `Wangshan Lu ` +* `we ` +* `Willson Mock ` +* `Will ` +* `wonyong kim ` +* `York Xiang ` + From a8c58a5000966f4a83363d37a822154bf630fb8e Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Fri, 20 Feb 2015 12:05:57 -0800 Subject: [PATCH 049/147] Fix names --- _posts/2015-02-20-Rust-1.0-alpha2.md | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/_posts/2015-02-20-Rust-1.0-alpha2.md b/_posts/2015-02-20-Rust-1.0-alpha2.md index 28bc33490..a7fdf4496 100644 --- a/_posts/2015-02-20-Rust-1.0-alpha2.md +++ b/_posts/2015-02-20-Rust-1.0-alpha2.md @@ -36,7 +36,7 @@ Thank you to all 207 contributors for this release: * `Aaron Turon ` * `Adam Roben ` -* `Adolfo Ochagavía ` +* `Adolfo Ochagavía ` * `Ahmed Charles ` * `Aidan Hobson Sayers ` * `Akos Kiss ` @@ -57,7 +57,7 @@ Thank you to all 207 contributors for this release: * `Barosl Lee ` * `Benjamin Peterson ` * `Ben S ` -* `Björn Steinbrink ` +* `Björn Steinbrink ` * `blackbeam ` * `bombless ` * `Brian Anderson ` @@ -83,7 +83,7 @@ Thank you to all 207 contributors for this release: * `Dan Yang ` * `David Creswick ` * `Diggory Blake ` -* `Dominik Inführ ` +* `Dominik Inführ ` * `Duane Edwards ` * `Duncan Regan ` * `Dzmitry Malyshau ` @@ -97,7 +97,7 @@ Thank you to all 207 contributors for this release: * `Eunji Jeong ` * `Felix S. Klock II ` * `Fenhl ` -* `Filip SzczepaÅ„ski ` +* `Filip Szczepański ` * `Flavio Percoco ` * `Florian Hahn ` * `Garrett Heel ` @@ -119,12 +119,12 @@ Thank you to all 207 contributors for this release: * `Jay True ` * `Jeff Belgum ` * `John Hodge ` -* `John KaÌŠre Alsaker ` -* `John KÃ¥re Alsaker ` +* `John Kåre Alsaker ` +* `John Kåre Alsaker ` * `Jonathan Reem ` * `JONNALAGADDA Srinivas ` * `Jorge Aparicio ` -* `Jorge Israel Peña ` +* `Jorge Israel Peña ` * `Jormundir ` * `Joseph Crail ` * `JP Sugarbroad ` @@ -138,7 +138,7 @@ Thank you to all 207 contributors for this release: * `Kevin Ballard ` * `Kevin Butler ` * `Kevin Yap ` -* `Kim Røen ` +* `Kim Røen ` * `klutzy ` * `Kostas Karachalios ` * `kud1ing ` @@ -147,16 +147,16 @@ Thank you to all 207 contributors for this release: * `Leo Testard ` * `Liigo Zhuang ` * `Logan Chien ` -* `Loïc Damien ` +* `Loïc Damien ` * `Luca Bruno ` * `Luke Francl ` * `Luke Steensen ` * `madmalik ` * `Manish Goregaokar ` * `Markus Siemens ` -* `Marvin Löbel ` +* `Marvin Löbel ` * `Matt Roche ` -* `Mátyás Mustoha ` +* `Mátyás Mustoha ` * `mdinger ` * `Michael Budde ` * `Michael Neumann ` @@ -203,7 +203,7 @@ Thank you to all 207 contributors for this release: * `Sean T Allen ` * `Sebastian Gesemann ` * `Sebastian Rasmussen ` -* `Sébastien Marie ` +* `Sébastien Marie ` * `Seo Sanghyeon ` * `Seth Faxon ` * `Simonas Kazlauskas ` @@ -222,7 +222,7 @@ Thank you to all 207 contributors for this release: * `Toby Scrace ` * `Tom Chittenden ` * `Tom Jakubowski ` -* `Toni Cárdenas ` +* `Toni Cárdenas ` * `Travis Watkins ` * `Tristan Storch ` * `Tshepang Lekhonkhobe ` @@ -241,4 +241,3 @@ Thank you to all 207 contributors for this release: * `Will ` * `wonyong kim ` * `York Xiang ` - From 27f2520e58373cea6e4b3ac7130c9d45b35a63da Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Fri, 20 Feb 2015 15:13:34 -0500 Subject: [PATCH 050/147] versioning is hard, consistent versioning is harder --- _posts/2015-02-20-Rust-1.0-alpha2.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/_posts/2015-02-20-Rust-1.0-alpha2.md b/_posts/2015-02-20-Rust-1.0-alpha2.md index a7fdf4496..51b5be9d2 100644 --- a/_posts/2015-02-20-Rust-1.0-alpha2.md +++ b/_posts/2015-02-20-Rust-1.0-alpha2.md @@ -1,8 +1,8 @@ --- layout: post -title: "Announcing Rust 1.0-alpha2" +title: "Announcing Rust 1.0.0.alpha.2" author: Steve Klabnik -description: "Rust 1.0-alpha2 has been released." +description: "Rust 1.0.0.alpha.2 has been released." --- Today, we are happy to announce the release of Rust 1.0.0.alpha.2! Rust is a From c755e9d2dc1f222f62213050af62b7857cbce1af Mon Sep 17 00:00:00 2001 From: elliottcf Date: Fri, 20 Feb 2015 14:55:47 -0600 Subject: [PATCH 051/147] Fixed word choice Replaced perusing or pursuing --- _posts/2015-02-20-Rust-1.0-alpha2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_posts/2015-02-20-Rust-1.0-alpha2.md b/_posts/2015-02-20-Rust-1.0-alpha2.md index 51b5be9d2..32b7f59e0 100644 --- a/_posts/2015-02-20-Rust-1.0-alpha2.md +++ b/_posts/2015-02-20-Rust-1.0-alpha2.md @@ -6,7 +6,7 @@ description: "Rust 1.0.0.alpha.2 has been released." --- Today, we are happy to announce the release of Rust 1.0.0.alpha.2! Rust is a -systems programming language perusing the trifecta: safe, fast, and concurrent. +systems programming language pursuing the trifecta: safe, fast, and concurrent. In accordance with our [status report](/2015/02/13/Final-1.0-timeline.html) last week, this is a second alpha release, rather than a first beta release. From 84fa18acd40a50c6b0a39790e9de2a7e491e925a Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Fri, 20 Feb 2015 16:08:40 -0500 Subject: [PATCH 052/147] Fix typo Thanks parley https://news.ycombinator.com/item?id=9082849 --- _posts/2015-02-20-Rust-1.0-alpha2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_posts/2015-02-20-Rust-1.0-alpha2.md b/_posts/2015-02-20-Rust-1.0-alpha2.md index 51b5be9d2..32b7f59e0 100644 --- a/_posts/2015-02-20-Rust-1.0-alpha2.md +++ b/_posts/2015-02-20-Rust-1.0-alpha2.md @@ -6,7 +6,7 @@ description: "Rust 1.0.0.alpha.2 has been released." --- Today, we are happy to announce the release of Rust 1.0.0.alpha.2! Rust is a -systems programming language perusing the trifecta: safe, fast, and concurrent. +systems programming language pursuing the trifecta: safe, fast, and concurrent. In accordance with our [status report](/2015/02/13/Final-1.0-timeline.html) last week, this is a second alpha release, rather than a first beta release. From e2cb108333687274fc1bd2f2d8f781d8bda0f8d9 Mon Sep 17 00:00:00 2001 From: sjef Date: Fri, 20 Feb 2015 23:20:17 +0100 Subject: [PATCH 053/147] Remove duplicate name in contributor list --- _posts/2015-02-20-Rust-1.0-alpha2.md | 1 - 1 file changed, 1 deletion(-) diff --git a/_posts/2015-02-20-Rust-1.0-alpha2.md b/_posts/2015-02-20-Rust-1.0-alpha2.md index 32b7f59e0..eff3c945a 100644 --- a/_posts/2015-02-20-Rust-1.0-alpha2.md +++ b/_posts/2015-02-20-Rust-1.0-alpha2.md @@ -119,7 +119,6 @@ Thank you to all 207 contributors for this release: * `Jay True ` * `Jeff Belgum ` * `John Hodge ` -* `John Kåre Alsaker ` * `John Kåre Alsaker ` * `Jonathan Reem ` * `JONNALAGADDA Srinivas ` From e0e6b44ff98707715e9a1699868cc27f68c7e588 Mon Sep 17 00:00:00 2001 From: Ingve Date: Fri, 20 Feb 2015 23:48:56 +0100 Subject: [PATCH 054/147] Remove duplicate name in contributor list Surely it is the same person. --- _posts/2015-02-20-Rust-1.0-alpha2.md | 1 - 1 file changed, 1 deletion(-) diff --git a/_posts/2015-02-20-Rust-1.0-alpha2.md b/_posts/2015-02-20-Rust-1.0-alpha2.md index eff3c945a..b4b748969 100644 --- a/_posts/2015-02-20-Rust-1.0-alpha2.md +++ b/_posts/2015-02-20-Rust-1.0-alpha2.md @@ -104,7 +104,6 @@ Thank you to all 207 contributors for this release: * `Geoffrey Thomas ` * `Greg Chapple ` * `Guillaume Gomez ` -* `GuillaumeGomez ` * `Guillaume Pinot ` * `Henrik Schopmans ` * `Hugo van der Wijst ` From 6f8a6b3a40c93a785f52be422be31585c19b7842 Mon Sep 17 00:00:00 2001 From: Ingve Date: Fri, 20 Feb 2015 23:52:49 +0100 Subject: [PATCH 055/147] Correct the amount of contributors --- _posts/2015-02-20-Rust-1.0-alpha2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_posts/2015-02-20-Rust-1.0-alpha2.md b/_posts/2015-02-20-Rust-1.0-alpha2.md index b4b748969..75f727b3e 100644 --- a/_posts/2015-02-20-Rust-1.0-alpha2.md +++ b/_posts/2015-02-20-Rust-1.0-alpha2.md @@ -32,7 +32,7 @@ in nightly. For more detail, please see the [Release notes](https://github.com/rust-lang/rust/blob/master/RELEASES.md#version-100-alpha2-february-2015). -Thank you to all 207 contributors for this release: +Thank you to all 205 contributors for this release: * `Aaron Turon ` * `Adam Roben ` From 6a5ee2c775957f81439512e60480e8ddf5ae7ff7 Mon Sep 17 00:00:00 2001 From: York Xiang Date: Sat, 21 Feb 2015 06:56:15 +0800 Subject: [PATCH 056/147] Remove duplicate contributor --- _posts/2015-01-09-Rust-1.0-alpha.md | 1 - _posts/2015-02-20-Rust-1.0-alpha2.md | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/_posts/2015-01-09-Rust-1.0-alpha.md b/_posts/2015-01-09-Rust-1.0-alpha.md index d89b7db91..657fec2d1 100644 --- a/_posts/2015-01-09-Rust-1.0-alpha.md +++ b/_posts/2015-01-09-Rust-1.0-alpha.md @@ -146,7 +146,6 @@ This alpha release could not have happened without the help of Rust's enthusiast * `Bheesham Persaud ` * `Björn Steinbrink ` * `bluss ` -* `bombless ` * `Boris Egorov ` * `bors ` * `Brandon Sanderson ` diff --git a/_posts/2015-02-20-Rust-1.0-alpha2.md b/_posts/2015-02-20-Rust-1.0-alpha2.md index eff3c945a..1e8d217f8 100644 --- a/_posts/2015-02-20-Rust-1.0-alpha2.md +++ b/_posts/2015-02-20-Rust-1.0-alpha2.md @@ -32,7 +32,7 @@ in nightly. For more detail, please see the [Release notes](https://github.com/rust-lang/rust/blob/master/RELEASES.md#version-100-alpha2-february-2015). -Thank you to all 207 contributors for this release: +Thank you to all 205 contributors for this release: * `Aaron Turon ` * `Adam Roben ` @@ -59,7 +59,6 @@ Thank you to all 207 contributors for this release: * `Ben S ` * `Björn Steinbrink ` * `blackbeam ` -* `bombless ` * `Brian Anderson ` * `Brian Leibig ` * `caipre ` From 0548c8e6459493e45dcc189e5d7458a0b0c10fa5 Mon Sep 17 00:00:00 2001 From: York Xiang Date: Sat, 21 Feb 2015 09:10:22 +0800 Subject: [PATCH 057/147] Correct amount of contributors --- _posts/2015-02-20-Rust-1.0-alpha2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_posts/2015-02-20-Rust-1.0-alpha2.md b/_posts/2015-02-20-Rust-1.0-alpha2.md index 3c578919b..b0e957681 100644 --- a/_posts/2015-02-20-Rust-1.0-alpha2.md +++ b/_posts/2015-02-20-Rust-1.0-alpha2.md @@ -32,7 +32,7 @@ in nightly. For more detail, please see the [Release notes](https://github.com/rust-lang/rust/blob/master/RELEASES.md#version-100-alpha2-february-2015). -Thank you to all 205 contributors for this release: +Thank you to all 204 contributors for this release: * `Aaron Turon ` * `Adam Roben ` From 2adc5f6dcbb77b1115f15e164e4a6f146e69bdec Mon Sep 17 00:00:00 2001 From: Matt Brubeck Date: Tue, 31 Mar 2015 15:44:37 -0700 Subject: [PATCH 058/147] Correct the date for 1.0-beta --- _posts/2015-02-13-Final-1.0-timeline.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/_posts/2015-02-13-Final-1.0-timeline.md b/_posts/2015-02-13-Final-1.0-timeline.md index c7db38c59..2eb65f074 100644 --- a/_posts/2015-02-13-Final-1.0-timeline.md +++ b/_posts/2015-02-13-Final-1.0-timeline.md @@ -17,7 +17,7 @@ committing to a precise release schedule for 1.0: * Rust 1.0-alpha2 -- Feb 20 * All 1.0 modules stable on nightly -- around Mar 9 -* Rust 1.0-beta -- Mar 31 +* Rust 1.0-beta -- Apr 3 * Rust 1.0 -- May 15 This schedule differs from the @@ -30,6 +30,10 @@ is that new path and IO APIs have only recently landed, and we would like more time to collect feedback before marking them stable. More details are below. +**Update:** An earlier version of this post listed Mar 31 as the 1.0-beta +release date, due to a miscalculation. The correct date is Apr 3, exactly six +weeks after alpha2 and six weeks before 1.0. + ## What's shipping in alpha2? We've managed to land almost all of the features From 3691d4cb325f7c1f29073bf1d309a8e817a0c904 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 3 Apr 2015 13:02:48 -0400 Subject: [PATCH 059/147] Add beta post --- _posts/2015-04-03-Rust-1.0-beta.md | 244 +++++++++++++++++++++++++++++ 1 file changed, 244 insertions(+) create mode 100644 _posts/2015-04-03-Rust-1.0-beta.md diff --git a/_posts/2015-04-03-Rust-1.0-beta.md b/_posts/2015-04-03-Rust-1.0-beta.md new file mode 100644 index 000000000..f8ed6c51b --- /dev/null +++ b/_posts/2015-04-03-Rust-1.0-beta.md @@ -0,0 +1,244 @@ +--- +layout: post +title: "Announcing Rust 1.0 Beta" +author: The Rust Core Team +--- + +Today we are excited to announce the [release of Rust 1.0 beta][ru]! +The beta release marks a very significant "state transition" in the +move towards 1.0. In particular, with the beta release, **all +libraries and language features that are planned to be stable for 1.0 +have been marked as stable**. As such, the beta release represents an +accurate preview of what Rust 1.0 will include. + +The Beta release also marks a turning point in our +[approach to stability][as]. During the alpha cycle, the use of +unstable APIs and language features was permitted, but triggered a +warning. As of the Beta release, the use of unstable APIs will become +an error (unless you are using Nightly builds or building from +source). + +The Rust ecosystem continues to grow. The +[crates.io](https://crates.io/) repository just passed 1 million +downloads and has over 1,700 crates available. Many of the top crates +in [crates.io](https://crates.io/) can now be built using only stable +Rust, and efforts to port the remainder are underway. Therefore, we +are now recommending that new users start with the Beta release, +rather than the Nightly builds, and the [rustup script][ru] will be +modified to install Beta by default. (However, it is easy to switch to +the Nightly build if some of your dependencies aren't updated yet. See +the [install page][ru] for details.) + +[ru]: http://www.rust-lang.org/install.html +[as]: http://blog.rust-lang.org/2014/10/30/Stability.html + +### What happens during the beta cycle? + +**The final Rust 1.0 release is scheduled for May 15th -- exactly six +weeks from now.** In the interim, we expect to put most of our effort +into fixing bugs, improving documentation and error messages, and +otherwise improving the end-user experience. We don't plan on making +functional changes to stable content, though naturally we may make +minor corrections or additions to the library APIs if shortcomings or +problems are uncovered (but the bar for such changes is relatively +high). + +While we don't expect to add any new features (or major new APIs) for +the 1.0 release, that doesn't mean we're going to stop working on them +altogether. In fact, quite the opposite! Per [the train model][tm], +the plan is to continue development on new features on the master +branch, in parallel with the beta. And of course, we'll be issuing the +beta for 1.1 release at the same time as we issue the final 1.0 +release, so you shouldn't have to wait long to start putting that work +to use. + +To help ensure that we don't accidentally introduce breakage as we add +new features, we've also been working on an exciting new CI +infrastructure to allow us to monitor which packages are building with +the Nightly builds and detect regressions across the entire Rust +ecosystem, not just our own test base. This infrastructure is still in +the development phase, but you can see a [sample report][sr] here. + +[tm]: http://blog.rust-lang.org/2014/12/12/1.0-Timeline.html +[sr]: https://gist.github.com/brson/a30a77836fbec057cbee + +### A community achievement + +As always, this Rust release is the achievement of the fantastic Rust +community at large. Thanks to everyone who has participated in the RFC +process, and a particular thank you to the 172 contributors for this +release: + +- `Aaron Turon ` +- `Aaron Weiss ` +- `Adam Jacob ` +- `Adenilson Cavalcanti ` +- `Adolfo Ochagavía ` +- `Ahmed Charles ` +- `Alan Cutter ` +- `Alex Crichton ` +- `Alexander Bliskovsky ` +- `Alexander Campbell ` +- `Alexander Chernyakhovsky ` +- `Alexis ` +- `Alexis Beingessner ` +- `Amol Mundayoor ` +- `Anders Kaseorg ` +- `Andrew Hobden ` +- `Andrew Paseltiner ` +- `Angus Lees ` +- `Barosl Lee ` +- `Björn Steinbrink ` +- `Brian Anderson ` +- `Brian Brooks ` +- `Brian Leibig ` +- `Camille Roussel ` +- `Camille TJHOA ` +- `Carol Nichols ` +- `Caspar Krieger ` +- `Ches Martin ` +- `Chloe <5paceToast@users.noreply.github.com>` +- `Chris Wong ` +- `Cody P Schafer ` +- `Corey Farwell ` +- `Corey Richardson ` +- `Dabo Ross ` +- `Dan Burkert ` +- `Dan Connolly ` +- `Dan W. <1danwade@gmail.com>` +- `Daniel Lobato García ` +- `Darin Morrison ` +- `Darrell Hamilton ` +- `Dave Huseby ` +- `David Creswick ` +- `David King ` +- `David Mally ` +- `Denis Defreyne ` +- `Drew Crawford ` +- `Dzmitry Malyshau ` +- `Eduard Bopp ` +- `Eduard Burtescu ` +- `Eduardo Bautista ` +- `Edward Wang ` +- `Emeliov Dmitrii ` +- `Eric Platon ` +- `Erick Tryzelaar ` +- `Eunji Jeong ` +- `Falco Hirschenberger ` +- `Felix S. Klock II ` +- `Fenhl ` +- `Flavio Percoco ` +- `Florian Hahn ` +- `Florian Hartwig ` +- `Florian Zeitz ` +- `FuGangqiang ` +- `Gary M. Josack ` +- `Germano Gabbianelli ` +- `GlacJAY ` +- `Gleb Kozyrev ` +- `Guillaume Gomez ` +- `GuillaumeGomez ` +- `Huachao Huang ` +- `Huon Wilson ` +- `Ivan Petkov ` +- `Ivan Radanov Ivanov ` +- `JP-Ellis ` +- `Jake Goulding ` +- `Jakub Bukaj ` +- `James Miller ` +- `Jessy Diamond Exum ` +- `Jihyun Yu ` +- `Johannes Oertel ` +- `John Hodge ` +- `John Zhang ` +- `Jonathan Reem ` +- `Jordan Woehr ` +- `Jorge Aparicio ` +- `Joseph Crail ` +- `Julian Orth ` +- `Julian Viereck ` +- `Junseok Lee ` +- `Kang Seonghoon ` +- `Keegan McAllister ` +- `Kevin Ballard ` +- `Kevin Butler ` +- `Kevin Yap ` +- `Lai Jiangshan ` +- `Leonids Maslovs ` +- `Liam Monahan ` +- `Liigo Zhuang ` +- `Manish Goregaokar ` +- `Markus Siemens ` +- `Markus Unterwaditzer ` +- `Marvin Löbel ` +- `Matt Brubeck ` +- `Matt Cox ` +- `Michael Woerister ` +- `Michał Krasnoborski ` +- `Mihnea Dobrescu-Balaur ` +- `Mikhail Zabaluev ` +- `Ms2ger ` +- `Murarth ` +- `Nicholas ` +- `Nicholas Bishop ` +- `Nicholas Mazzuca ` +- `Nick Cameron ` +- `Niko Matsakis ` +- `Oliver Schneider ` +- `Or Neeman ` +- `Pascal Hertleif ` +- `Patrick Walton ` +- `Paul ADENOT ` +- `Paul Osborne ` +- `Peter Elmers ` +- `Phil Dawes ` +- `Philip Munksgaard ` +- `Piotr Czarnecki ` +- `Pyry Kontio ` +- `Raphael Nestler ` +- `Ricardo Martins ` +- `Richard Diamond ` +- `Richo Healey ` +- `Ruud van Asseldonk ` +- `Ryan Prichard ` +- `Sae-bom Kim ` +- `Scott Olson ` +- `Sean McArthur ` +- `Seo Sanghyeon ` +- `Simonas Kazlauskas ` +- `Simonas Kazlauskas ` +- `Stepan Koltsov ` +- `Stepan Koltsov ` +- `Steve Klabnik ` +- `Steven Crockett ` +- `Steven Fackler ` +- `Sébastien Marie ` +- `Tamir Duberstein ` +- `Tero Hänninen ` +- `Tiago Nobrega ` +- `Tobias Bucher ` +- `Tom Jakubowski ` +- `Trent Nadeau ` +- `Tshepang Lekhonkhobe ` +- `Ulrik Sverdrup ` +- `Vadim Chugunov ` +- `Vadim Petrochenkov ` +- `Valerii Hiora ` +- `Vladimir Pouzanov ` +- `Vojtech Kral ` +- `Wangshan Lu ` +- `Wesley Wiser ` +- `York Xiang ` +- `awlnx ` +- `bcoopers ` +- `bombless ` +- `defuz ` +- `inrustwetrust ` +- `kgv ` +- `kjpgit ` +- `lummax ` +- `mdinger ` +- `nwin ` +- `ray glover ` +- `Łukasz Niemier ` + From 873dfc1f9e2899e58f05ce5601867eb0187b2fca Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Fri, 3 Apr 2015 14:06:30 -0400 Subject: [PATCH 060/147] uhhh release notes --- _posts/2015-04-03-Rust-1.0-beta.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/_posts/2015-04-03-Rust-1.0-beta.md b/_posts/2015-04-03-Rust-1.0-beta.md index f8ed6c51b..a313e8694 100644 --- a/_posts/2015-04-03-Rust-1.0-beta.md +++ b/_posts/2015-04-03-Rust-1.0-beta.md @@ -11,6 +11,10 @@ libraries and language features that are planned to be stable for 1.0 have been marked as stable**. As such, the beta release represents an accurate preview of what Rust 1.0 will include. +To see what has changed since 1.0-alpha2, please see the [release notes][rn]. + +[rn]: https://github.com/rust-lang/rust/blob/master/RELEASES.md#version-100-beta-april-2015 + The Beta release also marks a turning point in our [approach to stability][as]. During the alpha cycle, the use of unstable APIs and language features was permitted, but triggered a From ff8b73c5a3d29e9100b97a45691de2fcc1b93efe Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Fri, 3 Apr 2015 14:48:17 -0400 Subject: [PATCH 061/147] remove incorrect link Fixes #36 --- _posts/2014-10-30-Stability.md | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/_posts/2014-10-30-Stability.md b/_posts/2014-10-30-Stability.md index 250b76fc2..cfc43a9ce 100644 --- a/_posts/2014-10-30-Stability.md +++ b/_posts/2014-10-30-Stability.md @@ -57,12 +57,9 @@ stable -- with regular, frequent promotions from one channel to the next. New features and new APIs will be flagged as unstable via feature gates -and -[stability attributes](http://doc.rust-lang.org/reference.html#stability), -respectively. -Unstable features and standard library APIs will only be available on -the nightly branch, and only if you explicitly "opt in" to the -instability. +and stability attributes respectively. Unstable features and standard +library APIs will only be available on the nightly branch, and only if you +explicitly "opt in" to the instability. The beta and stable releases, on the other hand, will only include features and APIs deemed *stable*, which represents a commitment to From c030c4c8d80d3ca8e2e6720c2fc3c75b1fc73399 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Sun, 5 Apr 2015 23:34:51 +1000 Subject: [PATCH 062/147] Sort contributor list unicode-aware & folding case. --- _posts/2015-04-03-Rust-1.0-beta.md | 33 +++++++++++++++--------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/_posts/2015-04-03-Rust-1.0-beta.md b/_posts/2015-04-03-Rust-1.0-beta.md index a313e8694..867681c87 100644 --- a/_posts/2015-04-03-Rust-1.0-beta.md +++ b/_posts/2015-04-03-Rust-1.0-beta.md @@ -91,8 +91,11 @@ release: - `Andrew Hobden ` - `Andrew Paseltiner ` - `Angus Lees ` +- `awlnx ` - `Barosl Lee ` +- `bcoopers ` - `Björn Steinbrink ` +- `bombless ` - `Brian Anderson ` - `Brian Brooks ` - `Brian Leibig ` @@ -117,6 +120,7 @@ release: - `David Creswick ` - `David King ` - `David Mally ` +- `defuz ` - `Denis Defreyne ` - `Drew Crawford ` - `Dzmitry Malyshau ` @@ -144,9 +148,9 @@ release: - `GuillaumeGomez ` - `Huachao Huang ` - `Huon Wilson ` +- `inrustwetrust ` - `Ivan Petkov ` - `Ivan Radanov Ivanov ` -- `JP-Ellis ` - `Jake Goulding ` - `Jakub Bukaj ` - `James Miller ` @@ -159,6 +163,7 @@ release: - `Jordan Woehr ` - `Jorge Aparicio ` - `Joseph Crail ` +- `JP-Ellis ` - `Julian Orth ` - `Julian Viereck ` - `Junseok Lee ` @@ -167,27 +172,33 @@ release: - `Kevin Ballard ` - `Kevin Butler ` - `Kevin Yap ` +- `kgv ` +- `kjpgit ` - `Lai Jiangshan ` - `Leonids Maslovs ` - `Liam Monahan ` - `Liigo Zhuang ` +- `Łukasz Niemier ` +- `lummax ` - `Manish Goregaokar ` - `Markus Siemens ` - `Markus Unterwaditzer ` - `Marvin Löbel ` - `Matt Brubeck ` - `Matt Cox ` +- `mdinger ` - `Michael Woerister ` - `Michał Krasnoborski ` - `Mihnea Dobrescu-Balaur ` - `Mikhail Zabaluev ` - `Ms2ger ` - `Murarth ` -- `Nicholas ` - `Nicholas Bishop ` - `Nicholas Mazzuca ` +- `Nicholas ` - `Nick Cameron ` - `Niko Matsakis ` +- `nwin ` - `Oliver Schneider ` - `Or Neeman ` - `Pascal Hertleif ` @@ -200,6 +211,7 @@ release: - `Piotr Czarnecki ` - `Pyry Kontio ` - `Raphael Nestler ` +- `ray glover ` - `Ricardo Martins ` - `Richard Diamond ` - `Richo Healey ` @@ -208,15 +220,15 @@ release: - `Sae-bom Kim ` - `Scott Olson ` - `Sean McArthur ` +- `Sébastien Marie ` - `Seo Sanghyeon ` -- `Simonas Kazlauskas ` - `Simonas Kazlauskas ` +- `Simonas Kazlauskas ` - `Stepan Koltsov ` - `Stepan Koltsov ` - `Steve Klabnik ` - `Steven Crockett ` - `Steven Fackler ` -- `Sébastien Marie ` - `Tamir Duberstein ` - `Tero Hänninen ` - `Tiago Nobrega ` @@ -233,16 +245,3 @@ release: - `Wangshan Lu ` - `Wesley Wiser ` - `York Xiang ` -- `awlnx ` -- `bcoopers ` -- `bombless ` -- `defuz ` -- `inrustwetrust ` -- `kgv ` -- `kjpgit ` -- `lummax ` -- `mdinger ` -- `nwin ` -- `ray glover ` -- `Łukasz Niemier ` - From 411e7a690dd79defe40691e175c09d1043f3dbd9 Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Thu, 9 Apr 2015 17:06:59 -0700 Subject: [PATCH 063/147] Add blog post: Fearless Concurrency with Rust --- _posts/2015-04-10-Fearless-Concurrency.md | 591 ++++++++++++++++++++++ 1 file changed, 591 insertions(+) create mode 100644 _posts/2015-04-10-Fearless-Concurrency.md diff --git a/_posts/2015-04-10-Fearless-Concurrency.md b/_posts/2015-04-10-Fearless-Concurrency.md new file mode 100644 index 000000000..cb8e5ef51 --- /dev/null +++ b/_posts/2015-04-10-Fearless-Concurrency.md @@ -0,0 +1,591 @@ +--- +layout: post +title: "Fearless Concurrency with Rust" +author: Aaron Turon +--- + +Rust solves an apparent contradiction: safe systems programming. Its +secret weapon is *ownership*, a concept ubiquitous in systems +programming that Rust's compiler statically checks for you. The +result: you can program without a garbage collector *and* without fear +of segfaults, because Rust will catch your mistakes. + +**The same tools that make Rust safe also help tackle concurrency +head-on.** After all, use-after-free bugs and synchronization bugs +both happen when code accesses data it shouldn't -- and as we'll +see, ownership is all about access control. + +Here's a taste of concurrency in Rust: + +* A [channel][mpsc] transfers ownership of the messages sent along it, + so you can send a pointer from one thread to another without fear of + the threads later racing for access through that pointer. + +* A [lock][mutex] knows what data it protects, and Rust guarantees + that the data can only be accessed when the lock is held. + +* Every data type knows whether it can safely be [sent][send] between + or [accessed][sync] by multiple threads, and Rust enforces this safe + usage; there are no data races. + +* You can even [share stack frames][scoped] between threads, and Rust + will statically ensure that the frames remain active while other + threads are using them. + +None of the above benefits require any special knowledge to be built +in to the compiler. Locks, channels, semaphores, atomic operations and +so on are all *libraries* that take advantage of Rust's existing +features to provide the static checking underlying these strong +guarantees. + +The goal of this post is to give you some idea of how that's done. + +### Background: ownership + +> We'll start with an overview of Rust's ownership and borrowing +systems. If you're already familiar with these, you can skip the two +"background" sections and jump straight into concurrency. If you want +a deeper introduction, I can't recommend +[Yehuda Katz's post](http://blog.skylight.io/rust-means-never-having-to-close-a-socket/) +highly enough. And +[the Rust book](http://doc.rust-lang.org/book/ownership.html) has all +the details. + +In Rust, every value has an "owning scope," and passing or returning a +value means transferring ownership ("moving" it) to a new +scope. Values that are still owned when a scope ends are automatically +destroyed at that point. + +Let's look at some simple examples. Suppose we create a vector and +push some elements onto it: + +```rust +fn make_vec() { + let mut vec = Vec::new(); // owned by make_vec's scope + vec.push(0); + vec.push(1); + // scope ends, `vec` is destroyed +} +``` + +The scope that creates a value also initially owns it. In this case, +the `make_vec` function's body (the part within `{` and `}`) is the +owning scope for `vec`. The owner can do anything it likes with `vec`, +including mutating it by pushing. At the end of the scope, `vec` is +still owned, so it is automatically deallocated. + +Things get more interesting if the vector is returned or passed around: + +```rust +fn make_vec() -> Vec { + let mut vec = Vec::new(); + vec.push(0); + vec.push(1); + vec // transfer ownership to the caller +} + +fn print_vec(vec: Vec) { + // the `vec` parameter is part of this scope, so it's owned by `print_vec` + + for i in vec.iter() { + println!("{}", i) + } + + // now, `vec` is deallocated +} + +fn use_vec() { + let vec = make_vec(); // take ownership of the vector + print_vec(vec); // pass ownership to `print_vec` +} +``` + +Now, just before `make_vec`'s scope ends, `vec` is moved out by +returning it; it is not destroyed. A caller like `use_vec` then +receives ownership of the vector. + +On the other hand, the `print_vec` function takes a `vec` parameter, +and ownership of the vector is transferred *to* it by its +caller. Since `print_vec` does not transfer the ownership any further, +at the end of its scope the vector is destroyed. + +Once ownership has been given away, a value can no longer be used. For +example, consider this variant of `use_vec`: + +```rust +fn use_vec() { + let vec = make_vec(); // take ownership of the vector + print_vec(vec); // pass ownership to `print_vec` + + for i in vec.iter() { // continue using `vec` + println!("{}", i * 2) + } +} +``` + +If you feed this version to the compiler, you'll get an error: + +``` +error: use of moved value: `vec` + +for i in vec.iter() { + ^~~ +``` + +The compiler is saying `vec` is no longer available; ownership has +been transferred elsewhere. And that's very good, because the vector +has already been deallocated at this point! + +Disaster averted. + +### Background: borrowing + +The story so far isn't very satisfying, because it's not our intent +for `print_vec` to destroy the vector it was given. What we really +want is to grant `print_vec` *temporary* access to the vector, and +then continue using the vector afterwards. + +This is where *borrowing* comes in. If you have access to a value in +Rust, you can lend out that access to the functions you call. **Rust +will check that these leases do not outlive the object being +borrowed**. + +To borrow a value, you make a *reference* to it (a kind of pointer), +using the `&` operator: + +```rust +fn print_vec(vec: &Vec) { + // the `vec` parameter is borrowed for this scope + + for i in vec.iter() { + println!("{}", i) + } + + // now, the borrow ends +} + +fn use_vec() { + let vec = make_vec(); // take ownership of the vector + print_vec(&vec); // lend access to `print_vec` + for i in vec.iter() { // continue using `vec` + println!("{}", i * 2) + } + // vec is destroyed here +} +``` + +Now `print_vec` takes a reference to a vector, and `use_vec` lends out +the vector by writing `&vec`. Since borrows are temporary, `use_vec` +retains ownership of the vector; it can continue using it after the +call to `print_vec` returns (and its lease on `vec` has expired). + +Each reference is valid for a limited scope, which the compiler will +automatically determine. References come in two flavors: + +* Immutable references `&T`, which allow aliasing but not mutation. + There can be multiple `&T` references to the same value + simultaneously, but the value cannot be mutated while those + references are active. + +* Mutable references `&mut T`, which allow mutation but not aliasing. + If there is an `&mut T` reference to a value, there can be no other + active references at that time, but the value can be mutated. + +Rust checks these rules at compile time; borrowing has no runtime +overhead. + +Why have two kinds of references? Consider a function like: + +```rust +fn push_all(from: &Vec, to: &mut Vec) { + for i in from.iter() { + to.push(*i); + } +} +``` + +This function iterates over each element of one vector, pushing it +onto another. The iterator keeps a pointer into the vector at the +current and final positions, stepping one toward the other. + +Suppose we called this function with the same vector for both arguments: + +```rust +push_all(&vec, &mut vec) +``` + +This would spell disaster! As we're pushing elements onto the vector, +it will occasionally need to resize, allocating a new hunk of memory +and copying its elements over to it. The iterator would be left with a +dangling pointer into the old memory, leading to memory unsafety (with +attendant segfaults and worse). + +Fortunately, Rust ensures that **whenever a mutable borrow is active, +no other borrows of the object are active**, producing the message: + +``` +error: cannot borrow `vec` as mutable because it is also borrowed as immutable +push_all(&vec, &mut vec); + ^~~ +``` + +Disaster averted. + +### Message passing + +Now that we've covered the basic ownership story in Rust, let's see +what it means for concurrency. + +Concurrent programming comes in many styles, but a particularly simple +one is message passing, where threads or actors communicate by sending +each other messages. Proponents of the style emphasize the way that +it ties together sharing and communication: + +> Do not communicate by sharing memory; instead, share memory by +> communicating. +> +> --[Effective Go](http://golang.org/doc/effective_go.html) + +**Rust's ownership makes it easy to turn that advice into a +compiler-checked rule**. Consider the following channel API +([channels in Rust's standard library][mpsc] are a bit different): + +```rust +fn send(chan: &Channel, t: T); +fn recv(chan: &Channel) -> T; +``` + +Channels are generic over the type of data they transmit (the `` part of the API). The `Send` part means that `T` must be +considered safe to send between threads; we'll come back to that later +in the post, but for now it's enough to know that `Vec` is +`Send`. + +As always in Rust, passing in a `T` to the `send` function means +transferring ownership of it. This fact has profound consequences: it +means that code like the following will generate a compiler error. + +```rust +// Suppose chan: Channel> + +let mut vec = Vec::new(); +// do some computation +send(&chan, vec); +print_vec(&vec); +``` + +Here, the thread creates a vector, sends it to another thread, and +then continues using it. The thread receiving the vector could mutate +it as this thread continues running, so the call to `print_vec` could +lead to race condition or, for that matter, a use-after-free bug. + +Instead, the Rust compiler will produce an error message on the call +to `print_vec`: + +``` +Error: use of moved value `vec` +``` + +Disaster averted. + +### Locks + +Another way to deal with concurrency is by having threads communicate +through passive, shared state. + +Shared-state concurrency has a bad rap. It's easy to forget to acquire +a lock, or otherwise mutate the wrong data at the wrong time, with +disastrous results -- so easy that many eschew the style altogether. + +Rust's take is that: + +1. Shared-state concurrency is a fundamental programming style, needed +for systems code, for maximal performance, and for implementing +other styles of concurrency. + +2. The problem is really about *accidentally* shared state. + +Rust aims to give you the tools to conquer shared-state concurrency +directly. + +In Rust, threads are "isolated" from each other automatically, due to +ownership. Writes can only happen when the thread has mutable access, +either by owning the data, or by having a mutable borrow of it. Either +way, **the thread is guaranteed to be the only one with access at the +time**. To see how this plays out, let's look at locks. + +Remember that mutable borrows cannot occur simultaneously with other +borrows. Locks provide the same guarantee ("mutual exclusion") through +synchronization at runtime. That leads to a locking API that hooks +directly into Rust's ownership system. + +Here is a simplified version (the [standard library's][mutex] +is more ergonomic): + +```rust +// create a new mutes +fn mutex(t: T) -> Mutex; + +// acquire the lock +fn lock(mutex: &Mutex) -> MutexGuard; + +// access the data protected by the lock +fn access(guard: &mut MutexGuard) -> &mut T; +``` + +This lock API is unusual in several respects. + +First, the `Mutex` type is generic over a type `T` of **the data +protected by the lock**. When you create a `Mutex`, you transfer +ownership of that data *into* the mutex, immediately giving up access +to it. (Locks are unlocked when they are first created.) + +Later, you can `lock` to block the thread until the lock is +acquired. This function, too, is unusual in providing a return value, +`MutexGuard`. The `MutexGuard` automatically releases the lock when +it is destroyed; there is no separate `unlock` function. + +The only way to access the lock is through the `access` function, +which turns a mutable borrow of the guard into a mutable borrow of the +data (with a shorter lease): + +```rust +fn use_lock(mutex: &Mutex>) { + // acquire the lock, taking ownership of a guard; + // the lock is held for the rest of the scope + let mut guard = lock(mutex); + + // access the data by mutably borrowing the guard + let vec = access(&mut guard); + + // vec has type `&mut Vec` + vec.push(3); + + // lock automatically released here, when `guard` is destroyed +} +``` + +There are two key ingredients here: + +* The mutable reference returned by `access` cannot outlive the + `MutexGuard` it is borrowing from. + +* The lock is only released when the `MutexGuard` is destroyed. + +The result is that **Rust will not let you access lock-protected data +except when holding the lock**. Any attempt to do otherwise will +generate a compiler error. For example, consider the following buggy +"refactoring": + +```rust +fn use_lock(mutex: &Mutex>) { + let vec = { + // acquire the lock + let mut guard = lock(mutex); + + // attempt to return a borrow of the data + access(&mut guard) + + // guard is destroyed here, releasing the lock + }; + + // attempt to access the data outside of the lock. + vec.push(3); +} +``` + +Rust will generate an error pinpointing the problem: + +``` +error: `guard` does not live long enough +access(&mut guard) + ^~~~~ +``` + +Disaster averted. + +### Thread safety and `Send` + +It's typical to distinguish some data types as "thread safe" and +others not. Thread safe data structures use enough internal +synchronization to be safely used by multiple threads concurrently. + +For example, Rust ships with two kinds of "smart pointers" for +reference counting: + +* `Rc` provides reference counting via normal reads/writes. It is + not thread safe. + +* `Arc` provides reference counting via *atomic* operations. It is + thread safe. + +The hardware atomic operations used by `Arc` are generally more +expensive than the vanilla operations used by `Rc`, so it's +advantageous to use `Rc` rather than `Arc` wherever possible. On the +other hand, it's critical that an `Rc` never migrate from one +thread to another, because that could lead to race conditions that +corrupt the count. + +Usually, the only recourse is careful documentation; most languages +make no *actual* distinction between thread-safe and thread-unsafe +types. + +In Rust, the world is divided into two kinds of data types: those that +are [`Send`][send], meaning they can be safely moved from one thread to +another, and those that are `!Send`, meaning that it may not be safe +to do so. If all of a type's components are `Send`, so is that type -- +which covers most types. Certain base types are not inherently +thread-safe, though, so it's also possible to explicitly mark a type +like `Arc` as `Send`, saying to the compiler: "Trust me; I've verified +the necessary synchronization here." + +Naturally, `Arc` is `Send`, and `Rc` is not. + +We already saw that the `Channel` and `Mutex` APIs work only with +`Send` data. since they are the point at which data crosses thread +boundaries, they are also the point of enforcement for `Send`. + +Putting this all together, Rust programmers can reap the benefits of +`Rc` and other thread-unsafe types with confidence, knowing that if +they ever do accidentally try to send one to another thread, the Rust +compiler will say: + +``` +`Rc>` cannot be sent between threads safely +``` + +Disaster averted. + +### Sharing the stack: `scoped` + +So far, all the patterns we've seen involve creating data structures +on the heap that get shared between threads. But what if we wanted to +start some threads that make use of data living in our stack frame? +That could be dangerous: + +```rust +fn parent() { + let mut vec = Vec::new(); + // fill the vector + thread::spawn(|| { + print_vec(&vec) + }) +} +``` + +The child thread take a reference to `vec`, which in turn resides in +the stack frame of `parent`. When `parent` exists, the stack frame is +popped, but the child thread is none the wiser. Oops! + +To rule out such memory unsafety, Rust's basic thread spawning API +looks a bit like this: + +```rust +fn spawn(f: F) where F: 'static, ... +``` + +The `'static` constraint is a way of saying, roughly, that no borrowed +data is permitted in the closure. It means that a function like +`parent` above will generate an error: + +``` +error: `vec` does not live long enough +``` + +essentially catching the possibility of `parent`'s stack frame +popping. Disaster averted. + +But there is another way to guarantee safety: ensure that the parent +stack frame stays put until the child thread is done. This is the +pattern of *fork-join* programming, often used for divide-and-conquer +parallel algorithms. Rust supports it by providing a +["scoped"][scoped] variant of thread spawning: + +```rust +fn scoped<'a, F>(f: F) -> JoinGuard<'a> where F: 'a, ... +``` + +There are two key differences from the `spawn` API above: + +* The use a parameter `'a`, rather than `'static`. This parameter + represents a scope that encompasses all the borrows within the + closure, `f`. + +* The return value, a `JoinGuard`. As its name suggests, `JoinGuard` + ensures that the parent thread joins (waits on) its child, by + performing an implicit join in its destructor (if one hasn't happened + explicitly already). + +Including `'a` in `JoinGuard` ensures that the `JoinGuard` **cannot +escape the scope of any data borrowed by the closure**. In other +words, Rust guarantees that the parent thread waits for the child to +finish before popping any stack frames the child might have access to. + +Thus by adjusting our previous example, we can fix the bug and satisfy +the compiler: + +```rust +fn parent() { + let mut vec = Vec::new(); + // fill the vector + let guard = thread::scoped(|| { + print_vec(&vec) + }) + // guard destroyed here, implicitly joining +} +``` + +So in Rust, you can freely borrow stack data into child threads, +confident that the compiler will check for sufficient synchronization. + +### Data races + +At this point, we've seen enough to venture a strong statement about +Rust's approach to concurrency: the compiler prevents all *data races*. + +> A data race is any unsynchronized, concurrent access to data +> involving a write. + +Synchronization here includes things as low-level as atomic +instructions. Essentially, this is a way of saying that you cannot +accidentally "share state" between threads; all (mutating) access to +state has to be mediated by *some* form of synchronization. + +Data races are just one (very important) kind of race condition, but +by preventing them, Rust often helps you prevent other, more subtle +races as well. For example, it's often important that updates to +different locations appear to take place *atomically*: other threads +see either all of the updates, or none of them. In Rust, having `&mut` +access to the relevant locations at the same time **guarantees +atomicity of updates to them**, since no other thread could possibly +have concurrent read access. + +In short, ownership and borrowing give rise to *two* key value +propositions for Rust: + +* Memory safety without garbage collection. +* Concurrency without data races. + +### The future + +When Rust first began, it baked channels directly into the language, +taking a very opinionated stance on concurrency. + +In today's Rust, concurrency is *entirely* a library affair; +everything described in this post, including `Send`, is defined in the +standard library, and could be defined in an external library instead. + +And that's very exciting, because it means that Rust's concurrency +story can endlessly evolve, growing to encompass new paradigms and +catch new classes of bugs. Libraries like [syncbox][syncbox] and +[simple_parallel][simple_parallel] are taking some of the first steps, +and we expect to invest heavily in this space in the next few +months. Stay tuned! + +[mpsc]: http://static.rust-lang.org/doc/master/std/sync/mpsc/index.html +[mutex]: http://static.rust-lang.org/doc/master/std/sync/struct.Mutex.html +[send]: http://static.rust-lang.org/doc/master/std/marker/trait.Send.html +[sync]: http://static.rust-lang.org/doc/master/std/marker/trait.Sync.html +[scoped]: http://static.rust-lang.org/doc/master/std/thread/fn.scoped.html +[syncbox]: https://github.com/carllerche/syncbox +[simple_parallel]: https://github.com/huonw/simple_parallel From 6eaf25bb0a5a4a0b1fdbae90ef4e7ccaa4c6d6c0 Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Fri, 10 Apr 2015 01:13:50 -0700 Subject: [PATCH 064/147] Revisions to intro --- _posts/2015-04-10-Fearless-Concurrency.md | 56 +++++++++++++++-------- 1 file changed, 37 insertions(+), 19 deletions(-) diff --git a/_posts/2015-04-10-Fearless-Concurrency.md b/_posts/2015-04-10-Fearless-Concurrency.md index cb8e5ef51..243390f6e 100644 --- a/_posts/2015-04-10-Fearless-Concurrency.md +++ b/_posts/2015-04-10-Fearless-Concurrency.md @@ -4,39 +4,57 @@ title: "Fearless Concurrency with Rust" author: Aaron Turon --- -Rust solves an apparent contradiction: safe systems programming. Its -secret weapon is *ownership*, a concept ubiquitous in systems -programming that Rust's compiler statically checks for you. The -result: you can program without a garbage collector *and* without fear -of segfaults, because Rust will catch your mistakes. +The Rust project was initiated to solve two thorny problems: -**The same tools that make Rust safe also help tackle concurrency -head-on.** After all, use-after-free bugs and synchronization bugs -both happen when code accesses data it shouldn't -- and as we'll -see, ownership is all about access control. +* How do you do safe systems programming? +* How do you do make concurrency painless? + +Initially these problems seemed orthogonal, but to our amazement, they +turned out to really be the same: **the same tools that make Rust safe +also help you tackle concurrency head-on**. + +Memory safety bugs and concurrency bugs often come down to code +accessing data when it shouldn't. Rust's secret weapon is *ownership*, +a discipline for access control that systems programmers try to +follow, but that Rust's compiler checks statically for you. + +For memory safety, this means you can program without a garbage +collector *and* without fear of segfaults, because Rust will catch +your mistakes. + +For concurrency, this means you can choose from a wide variety of +paradigms (message passing, shared state, lock-free, purely +functional), and Rust's ownership will help you avoid the associated +pitfalls. Here's a taste of concurrency in Rust: * A [channel][mpsc] transfers ownership of the messages sent along it, so you can send a pointer from one thread to another without fear of - the threads later racing for access through that pointer. + the threads later racing for access through that pointer. **Channels + enforce thread isolation.** * A [lock][mutex] knows what data it protects, and Rust guarantees - that the data can only be accessed when the lock is held. + that the data can only be accessed when the lock is held. State is + never accidentally shared. **"Lock data, not code" is enforced in + Rust.** * Every data type knows whether it can safely be [sent][send] between or [accessed][sync] by multiple threads, and Rust enforces this safe - usage; there are no data races. + usage; there are no data races, even for lock-free data structures. + **Thread safety isn't just documentation; it's law.** * You can even [share stack frames][scoped] between threads, and Rust will statically ensure that the frames remain active while other - threads are using them. - -None of the above benefits require any special knowledge to be built -in to the compiler. Locks, channels, semaphores, atomic operations and -so on are all *libraries* that take advantage of Rust's existing -features to provide the static checking underlying these strong -guarantees. + threads are using them. **Even the most daring forms of sharing are + guaranteed safe in Rust**. + +All of these benefits come out of Rust's ownership model, and in fact +locks, channels, lock-fee data structures and so on are defined in +libraries, not the core language. That means that Rust's approach to +concurrency is *open ended*: new libraries can embrace new paradigms and +catch new bugs, just by adding APIs that take advantage of Rust's +general ownership features. The goal of this post is to give you some idea of how that's done. From 17c678d7fcfe11d41d1836fe4665a15c158f76d9 Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Fri, 10 Apr 2015 01:53:26 -0700 Subject: [PATCH 065/147] Add description field --- _posts/2015-04-10-Fearless-Concurrency.md | 1 + 1 file changed, 1 insertion(+) diff --git a/_posts/2015-04-10-Fearless-Concurrency.md b/_posts/2015-04-10-Fearless-Concurrency.md index 243390f6e..c1670f30d 100644 --- a/_posts/2015-04-10-Fearless-Concurrency.md +++ b/_posts/2015-04-10-Fearless-Concurrency.md @@ -2,6 +2,7 @@ layout: post title: "Fearless Concurrency with Rust" author: Aaron Turon +description: "Rust's vision for concurrency" --- The Rust project was initiated to solve two thorny problems: From 64fbfd921d66d5b9525a7ebb09ae6fcd6d77a59c Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Fri, 10 Apr 2015 09:20:49 -0700 Subject: [PATCH 066/147] Final round of revisions --- _posts/2015-04-10-Fearless-Concurrency.md | 79 ++++++++++++----------- 1 file changed, 41 insertions(+), 38 deletions(-) diff --git a/_posts/2015-04-10-Fearless-Concurrency.md b/_posts/2015-04-10-Fearless-Concurrency.md index c1670f30d..e81c1f4d2 100644 --- a/_posts/2015-04-10-Fearless-Concurrency.md +++ b/_posts/2015-04-10-Fearless-Concurrency.md @@ -10,9 +10,9 @@ The Rust project was initiated to solve two thorny problems: * How do you do safe systems programming? * How do you do make concurrency painless? -Initially these problems seemed orthogonal, but to our amazement, they -turned out to really be the same: **the same tools that make Rust safe -also help you tackle concurrency head-on**. +Initially these problems seemed orthogonal, but to our amazement, the +solution turned out to be identical: **the same tools that make Rust +safe also help you tackle concurrency head-on**. Memory safety bugs and concurrency bugs often come down to code accessing data when it shouldn't. Rust's secret weapon is *ownership*, @@ -25,15 +25,14 @@ your mistakes. For concurrency, this means you can choose from a wide variety of paradigms (message passing, shared state, lock-free, purely -functional), and Rust's ownership will help you avoid the associated -pitfalls. +functional), and Rust will help you avoid common pitfalls. Here's a taste of concurrency in Rust: * A [channel][mpsc] transfers ownership of the messages sent along it, so you can send a pointer from one thread to another without fear of - the threads later racing for access through that pointer. **Channels - enforce thread isolation.** + the threads later racing for access through that pointer. **Rust's + channels enforce thread isolation.** * A [lock][mutex] knows what data it protects, and Rust guarantees that the data can only be accessed when the lock is held. State is @@ -53,9 +52,9 @@ Here's a taste of concurrency in Rust: All of these benefits come out of Rust's ownership model, and in fact locks, channels, lock-fee data structures and so on are defined in libraries, not the core language. That means that Rust's approach to -concurrency is *open ended*: new libraries can embrace new paradigms and -catch new bugs, just by adding APIs that take advantage of Rust's -general ownership features. +concurrency is *open ended*: new libraries can embrace new paradigms +and catch new bugs, just by adding APIs that use Rust's ownership +features. The goal of this post is to give you some idea of how that's done. @@ -88,10 +87,10 @@ fn make_vec() { ``` The scope that creates a value also initially owns it. In this case, -the `make_vec` function's body (the part within `{` and `}`) is the -owning scope for `vec`. The owner can do anything it likes with `vec`, -including mutating it by pushing. At the end of the scope, `vec` is -still owned, so it is automatically deallocated. +the body of `make_vec` is the owning scope for `vec`. The owner can do +anything it likes with `vec`, including mutating it by pushing. At the +end of the scope, `vec` is still owned, so it is automatically +deallocated. Things get more interesting if the vector is returned or passed around: @@ -159,7 +158,7 @@ Disaster averted. ### Background: borrowing -The story so far isn't very satisfying, because it's not our intent +The story so far isn't totally satisfying, because it's not our intent for `print_vec` to destroy the vector it was given. What we really want is to grant `print_vec` *temporary* access to the vector, and then continue using the vector afterwards. @@ -201,12 +200,12 @@ call to `print_vec` returns (and its lease on `vec` has expired). Each reference is valid for a limited scope, which the compiler will automatically determine. References come in two flavors: -* Immutable references `&T`, which allow aliasing but not mutation. +* Immutable references `&T`, which allow sharing but not mutation. There can be multiple `&T` references to the same value simultaneously, but the value cannot be mutated while those references are active. -* Mutable references `&mut T`, which allow mutation but not aliasing. +* Mutable references `&mut T`, which allow mutation but not sharing. If there is an `&mut T` reference to a value, there can be no other active references at that time, but the value can be mutated. @@ -227,7 +226,7 @@ This function iterates over each element of one vector, pushing it onto another. The iterator keeps a pointer into the vector at the current and final positions, stepping one toward the other. -Suppose we called this function with the same vector for both arguments: +What if we called this function with the same vector for both arguments? ```rust push_all(&vec, &mut vec) @@ -237,7 +236,7 @@ This would spell disaster! As we're pushing elements onto the vector, it will occasionally need to resize, allocating a new hunk of memory and copying its elements over to it. The iterator would be left with a dangling pointer into the old memory, leading to memory unsafety (with -attendant segfaults and worse). +attendant segfaults or worse). Fortunately, Rust ensures that **whenever a mutable borrow is active, no other borrows of the object are active**, producing the message: @@ -318,14 +317,14 @@ disastrous results -- so easy that many eschew the style altogether. Rust's take is that: -1. Shared-state concurrency is a fundamental programming style, needed -for systems code, for maximal performance, and for implementing -other styles of concurrency. +1. Shared-state concurrency is nevertheless a fundamental programming +style, needed for systems code, for maximal performance, and for +implementing other styles of concurrency. 2. The problem is really about *accidentally* shared state. Rust aims to give you the tools to conquer shared-state concurrency -directly. +directly, whether you're using locking or lock-free techniques. In Rust, threads are "isolated" from each other automatically, due to ownership. Writes can only happen when the thread has mutable access, @@ -391,10 +390,10 @@ There are two key ingredients here: * The lock is only released when the `MutexGuard` is destroyed. -The result is that **Rust will not let you access lock-protected data -except when holding the lock**. Any attempt to do otherwise will -generate a compiler error. For example, consider the following buggy -"refactoring": +The result is that **Rust enforces locking discipline: it will not let +you access lock-protected data except when holding the lock**. Any +attempt to do otherwise will generate a compiler error. For example, +consider the following buggy "refactoring": ```rust fn use_lock(mutex: &Mutex>) { @@ -438,15 +437,14 @@ reference counting: * `Arc` provides reference counting via *atomic* operations. It is thread safe. -The hardware atomic operations used by `Arc` are generally more -expensive than the vanilla operations used by `Rc`, so it's -advantageous to use `Rc` rather than `Arc` wherever possible. On the -other hand, it's critical that an `Rc` never migrate from one -thread to another, because that could lead to race conditions that -corrupt the count. +The hardware atomic operations used by `Arc` are more expensive than +the vanilla operations used by `Rc`, so it's advantageous to use `Rc` +rather than `Arc`. On the other hand, it's critical that an `Rc` +never migrate from one thread to another, because that could lead to +race conditions that corrupt the count. Usually, the only recourse is careful documentation; most languages -make no *actual* distinction between thread-safe and thread-unsafe +make no *semantic* distinction between thread-safe and thread-unsafe types. In Rust, the world is divided into two kinds of data types: those that @@ -465,7 +463,7 @@ We already saw that the `Channel` and `Mutex` APIs work only with boundaries, they are also the point of enforcement for `Send`. Putting this all together, Rust programmers can reap the benefits of -`Rc` and other thread-unsafe types with confidence, knowing that if +`Rc` and other thread-*unsafe* types with confidence, knowing that if they ever do accidentally try to send one to another thread, the Rust compiler will say: @@ -560,7 +558,7 @@ confident that the compiler will check for sufficient synchronization. ### Data races At this point, we've seen enough to venture a strong statement about -Rust's approach to concurrency: the compiler prevents all *data races*. +Rust's approach to concurrency: **the compiler prevents all *data races*.** > A data race is any unsynchronized, concurrent access to data > involving a write. @@ -579,8 +577,13 @@ access to the relevant locations at the same time **guarantees atomicity of updates to them**, since no other thread could possibly have concurrent read access. -In short, ownership and borrowing give rise to *two* key value -propositions for Rust: +It's worth pausing for a moment to think about this guarantee in the +broader landscape of languages. Many languages provide memory safety +through garbage collection. But garbage collection doesn't give you +any help in preventing data races. + +Rust instead uses ownership and borrowing to provide its two key value +propositions: * Memory safety without garbage collection. * Concurrency without data races. From b35727916027e99fc2f601ff7e9fc09fd8ef14a3 Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Fri, 10 Apr 2015 09:31:15 -0700 Subject: [PATCH 067/147] Fix code fence problem --- _posts/2015-04-10-Fearless-Concurrency.md | 84 +++++++++++------------ 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/_posts/2015-04-10-Fearless-Concurrency.md b/_posts/2015-04-10-Fearless-Concurrency.md index e81c1f4d2..0808053e7 100644 --- a/_posts/2015-04-10-Fearless-Concurrency.md +++ b/_posts/2015-04-10-Fearless-Concurrency.md @@ -77,14 +77,14 @@ destroyed at that point. Let's look at some simple examples. Suppose we create a vector and push some elements onto it: -```rust +~~~~rust fn make_vec() { let mut vec = Vec::new(); // owned by make_vec's scope vec.push(0); vec.push(1); // scope ends, `vec` is destroyed } -``` +~~~~ The scope that creates a value also initially owns it. In this case, the body of `make_vec` is the owning scope for `vec`. The owner can do @@ -94,7 +94,7 @@ deallocated. Things get more interesting if the vector is returned or passed around: -```rust +~~~~rust fn make_vec() -> Vec { let mut vec = Vec::new(); vec.push(0); @@ -116,7 +116,7 @@ fn use_vec() { let vec = make_vec(); // take ownership of the vector print_vec(vec); // pass ownership to `print_vec` } -``` +~~~~ Now, just before `make_vec`'s scope ends, `vec` is moved out by returning it; it is not destroyed. A caller like `use_vec` then @@ -130,7 +130,7 @@ at the end of its scope the vector is destroyed. Once ownership has been given away, a value can no longer be used. For example, consider this variant of `use_vec`: -```rust +~~~~rust fn use_vec() { let vec = make_vec(); // take ownership of the vector print_vec(vec); // pass ownership to `print_vec` @@ -139,16 +139,16 @@ fn use_vec() { println!("{}", i * 2) } } -``` +~~~~ If you feed this version to the compiler, you'll get an error: -``` +~~~~ error: use of moved value: `vec` for i in vec.iter() { ^~~ -``` +~~~~ The compiler is saying `vec` is no longer available; ownership has been transferred elsewhere. And that's very good, because the vector @@ -171,7 +171,7 @@ borrowed**. To borrow a value, you make a *reference* to it (a kind of pointer), using the `&` operator: -```rust +~~~~rust fn print_vec(vec: &Vec) { // the `vec` parameter is borrowed for this scope @@ -190,7 +190,7 @@ fn use_vec() { } // vec is destroyed here } -``` +~~~~ Now `print_vec` takes a reference to a vector, and `use_vec` lends out the vector by writing `&vec`. Since borrows are temporary, `use_vec` @@ -214,13 +214,13 @@ overhead. Why have two kinds of references? Consider a function like: -```rust +~~~~rust fn push_all(from: &Vec, to: &mut Vec) { for i in from.iter() { to.push(*i); } } -``` +~~~~ This function iterates over each element of one vector, pushing it onto another. The iterator keeps a pointer into the vector at the @@ -228,9 +228,9 @@ current and final positions, stepping one toward the other. What if we called this function with the same vector for both arguments? -```rust +~~~~rust push_all(&vec, &mut vec) -``` +~~~~ This would spell disaster! As we're pushing elements onto the vector, it will occasionally need to resize, allocating a new hunk of memory @@ -241,11 +241,11 @@ attendant segfaults or worse). Fortunately, Rust ensures that **whenever a mutable borrow is active, no other borrows of the object are active**, producing the message: -``` +~~~~ error: cannot borrow `vec` as mutable because it is also borrowed as immutable push_all(&vec, &mut vec); ^~~ -``` +~~~~ Disaster averted. @@ -268,10 +268,10 @@ it ties together sharing and communication: compiler-checked rule**. Consider the following channel API ([channels in Rust's standard library][mpsc] are a bit different): -```rust +~~~~rust fn send(chan: &Channel, t: T); fn recv(chan: &Channel) -> T; -``` +~~~~ Channels are generic over the type of data they transmit (the `` part of the API). The `Send` part means that `T` must be @@ -283,14 +283,14 @@ As always in Rust, passing in a `T` to the `send` function means transferring ownership of it. This fact has profound consequences: it means that code like the following will generate a compiler error. -```rust +~~~~rust // Suppose chan: Channel> let mut vec = Vec::new(); // do some computation send(&chan, vec); print_vec(&vec); -``` +~~~~ Here, the thread creates a vector, sends it to another thread, and then continues using it. The thread receiving the vector could mutate @@ -300,9 +300,9 @@ lead to race condition or, for that matter, a use-after-free bug. Instead, the Rust compiler will produce an error message on the call to `print_vec`: -``` +~~~~ Error: use of moved value `vec` -``` +~~~~ Disaster averted. @@ -340,7 +340,7 @@ directly into Rust's ownership system. Here is a simplified version (the [standard library's][mutex] is more ergonomic): -```rust +~~~~rust // create a new mutes fn mutex(t: T) -> Mutex; @@ -349,7 +349,7 @@ fn lock(mutex: &Mutex) -> MutexGuard; // access the data protected by the lock fn access(guard: &mut MutexGuard) -> &mut T; -``` +~~~~ This lock API is unusual in several respects. @@ -367,7 +367,7 @@ The only way to access the lock is through the `access` function, which turns a mutable borrow of the guard into a mutable borrow of the data (with a shorter lease): -```rust +~~~~rust fn use_lock(mutex: &Mutex>) { // acquire the lock, taking ownership of a guard; // the lock is held for the rest of the scope @@ -381,7 +381,7 @@ fn use_lock(mutex: &Mutex>) { // lock automatically released here, when `guard` is destroyed } -``` +~~~~ There are two key ingredients here: @@ -395,7 +395,7 @@ you access lock-protected data except when holding the lock**. Any attempt to do otherwise will generate a compiler error. For example, consider the following buggy "refactoring": -```rust +~~~~rust fn use_lock(mutex: &Mutex>) { let vec = { // acquire the lock @@ -410,15 +410,15 @@ fn use_lock(mutex: &Mutex>) { // attempt to access the data outside of the lock. vec.push(3); } -``` +~~~~ Rust will generate an error pinpointing the problem: -``` +~~~~ error: `guard` does not live long enough access(&mut guard) ^~~~~ -``` +~~~~ Disaster averted. @@ -467,9 +467,9 @@ Putting this all together, Rust programmers can reap the benefits of they ever do accidentally try to send one to another thread, the Rust compiler will say: -``` +~~~~ `Rc>` cannot be sent between threads safely -``` +~~~~ Disaster averted. @@ -480,7 +480,7 @@ on the heap that get shared between threads. But what if we wanted to start some threads that make use of data living in our stack frame? That could be dangerous: -```rust +~~~~rust fn parent() { let mut vec = Vec::new(); // fill the vector @@ -488,7 +488,7 @@ fn parent() { print_vec(&vec) }) } -``` +~~~~ The child thread take a reference to `vec`, which in turn resides in the stack frame of `parent`. When `parent` exists, the stack frame is @@ -497,17 +497,17 @@ popped, but the child thread is none the wiser. Oops! To rule out such memory unsafety, Rust's basic thread spawning API looks a bit like this: -```rust +~~~~rust fn spawn(f: F) where F: 'static, ... -``` +~~~~ The `'static` constraint is a way of saying, roughly, that no borrowed data is permitted in the closure. It means that a function like `parent` above will generate an error: -``` +~~~~ error: `vec` does not live long enough -``` +~~~~ essentially catching the possibility of `parent`'s stack frame popping. Disaster averted. @@ -518,9 +518,9 @@ pattern of *fork-join* programming, often used for divide-and-conquer parallel algorithms. Rust supports it by providing a ["scoped"][scoped] variant of thread spawning: -```rust +~~~~rust fn scoped<'a, F>(f: F) -> JoinGuard<'a> where F: 'a, ... -``` +~~~~ There are two key differences from the `spawn` API above: @@ -541,7 +541,7 @@ finish before popping any stack frames the child might have access to. Thus by adjusting our previous example, we can fix the bug and satisfy the compiler: -```rust +~~~~rust fn parent() { let mut vec = Vec::new(); // fill the vector @@ -550,7 +550,7 @@ fn parent() { }) // guard destroyed here, implicitly joining } -``` +~~~~ So in Rust, you can freely borrow stack data into child threads, confident that the compiler will check for sufficient synchronization. From 467f5d7d2543d55031b599ee00e55b36192e3c26 Mon Sep 17 00:00:00 2001 From: Ricardo Mendes Date: Fri, 10 Apr 2015 17:40:44 +0100 Subject: [PATCH 068/147] Typo in code comment, mutes -> mutex --- _posts/2015-04-10-Fearless-Concurrency.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_posts/2015-04-10-Fearless-Concurrency.md b/_posts/2015-04-10-Fearless-Concurrency.md index 0808053e7..c3eb59c37 100644 --- a/_posts/2015-04-10-Fearless-Concurrency.md +++ b/_posts/2015-04-10-Fearless-Concurrency.md @@ -341,7 +341,7 @@ Here is a simplified version (the [standard library's][mutex] is more ergonomic): ~~~~rust -// create a new mutes +// create a new mutex fn mutex(t: T) -> Mutex; // acquire the lock From c21d868d58bf834a9d4ff5d06103263c89b07363 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Fri, 10 Apr 2015 09:50:51 -0700 Subject: [PATCH 069/147] Set up highlighting --- _config.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/_config.yml b/_config.yml index 8f9f594d4..ece89c2c2 100644 --- a/_config.yml +++ b/_config.yml @@ -9,6 +9,7 @@ twitter_username: rustlang github_username: rust-lang # Build settings -markdown: kramdown +highlighter: pygments +markdown: redcarpet root: http://blog.rust-lang.org From 35a85407f1880cd99e3a4679f3bc353e57d0965e Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Fri, 10 Apr 2015 09:54:22 -0700 Subject: [PATCH 070/147] Adjust section headers to avoid code --- _posts/2015-04-10-Fearless-Concurrency.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/_posts/2015-04-10-Fearless-Concurrency.md b/_posts/2015-04-10-Fearless-Concurrency.md index c3eb59c37..29f180a3f 100644 --- a/_posts/2015-04-10-Fearless-Concurrency.md +++ b/_posts/2015-04-10-Fearless-Concurrency.md @@ -422,7 +422,7 @@ access(&mut guard) Disaster averted. -### Thread safety and `Send` +### Thread safety and "Send" It's typical to distinguish some data types as "thread safe" and others not. Thread safe data structures use enough internal @@ -473,7 +473,7 @@ compiler will say: Disaster averted. -### Sharing the stack: `scoped` +### Sharing the stack: "scoped" So far, all the patterns we've seen involve creating data structures on the heap that get shared between threads. But what if we wanted to From 2e1b32ae1e8a1a2a655e070d0164d59b56609f99 Mon Sep 17 00:00:00 2001 From: Chas Date: Fri, 10 Apr 2015 10:17:07 -0700 Subject: [PATCH 071/147] Fixed typo in 2015-04-10-Fearless-Concurrency.md lock-fee should be lock-free --- _posts/2015-04-10-Fearless-Concurrency.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_posts/2015-04-10-Fearless-Concurrency.md b/_posts/2015-04-10-Fearless-Concurrency.md index 29f180a3f..e0d089c0c 100644 --- a/_posts/2015-04-10-Fearless-Concurrency.md +++ b/_posts/2015-04-10-Fearless-Concurrency.md @@ -50,7 +50,7 @@ Here's a taste of concurrency in Rust: guaranteed safe in Rust**. All of these benefits come out of Rust's ownership model, and in fact -locks, channels, lock-fee data structures and so on are defined in +locks, channels, lock-free data structures and so on are defined in libraries, not the core language. That means that Rust's approach to concurrency is *open ended*: new libraries can embrace new paradigms and catch new bugs, just by adding APIs that use Rust's ownership From 3791406dda9ef9c0327ee8e35a0b54a4718850ed Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Fri, 10 Apr 2015 10:43:29 -0700 Subject: [PATCH 072/147] Fix uncapitalized letter --- _posts/2015-04-10-Fearless-Concurrency.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_posts/2015-04-10-Fearless-Concurrency.md b/_posts/2015-04-10-Fearless-Concurrency.md index c3eb59c37..ec5139013 100644 --- a/_posts/2015-04-10-Fearless-Concurrency.md +++ b/_posts/2015-04-10-Fearless-Concurrency.md @@ -459,7 +459,7 @@ the necessary synchronization here." Naturally, `Arc` is `Send`, and `Rc` is not. We already saw that the `Channel` and `Mutex` APIs work only with -`Send` data. since they are the point at which data crosses thread +`Send` data. Since they are the point at which data crosses thread boundaries, they are also the point of enforcement for `Send`. Putting this all together, Rust programmers can reap the benefits of From 92e8e6994fa8fdd95afac0c202c1de26f342340b Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Fri, 10 Apr 2015 10:54:16 -0700 Subject: [PATCH 073/147] Add missing semicolon --- _posts/2015-04-10-Fearless-Concurrency.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_posts/2015-04-10-Fearless-Concurrency.md b/_posts/2015-04-10-Fearless-Concurrency.md index c3eb59c37..8d7065c06 100644 --- a/_posts/2015-04-10-Fearless-Concurrency.md +++ b/_posts/2015-04-10-Fearless-Concurrency.md @@ -547,7 +547,7 @@ fn parent() { // fill the vector let guard = thread::scoped(|| { print_vec(&vec) - }) + }); // guard destroyed here, implicitly joining } ~~~~ From 55b7f97982c52369f8f04517959307409ff62aa6 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Fri, 10 Apr 2015 15:57:10 -0400 Subject: [PATCH 074/147] spelling fix --- _posts/2015-04-10-Fearless-Concurrency.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_posts/2015-04-10-Fearless-Concurrency.md b/_posts/2015-04-10-Fearless-Concurrency.md index b71001836..e9ce1aa3c 100644 --- a/_posts/2015-04-10-Fearless-Concurrency.md +++ b/_posts/2015-04-10-Fearless-Concurrency.md @@ -491,7 +491,7 @@ fn parent() { ~~~~ The child thread take a reference to `vec`, which in turn resides in -the stack frame of `parent`. When `parent` exists, the stack frame is +the stack frame of `parent`. When `parent` exits, the stack frame is popped, but the child thread is none the wiser. Oops! To rule out such memory unsafety, Rust's basic thread spawning API From c2472886b715b4664654d2997637e3b0bfe1092f Mon Sep 17 00:00:00 2001 From: Daniel Hackney Date: Fri, 10 Apr 2015 15:15:53 -0700 Subject: [PATCH 075/147] Remove redundant verb "How do you do make ..." doesn't make sense. Dropping the "do" fixes the sentence. --- _posts/2015-04-10-Fearless-Concurrency.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_posts/2015-04-10-Fearless-Concurrency.md b/_posts/2015-04-10-Fearless-Concurrency.md index e9ce1aa3c..4e7635eae 100644 --- a/_posts/2015-04-10-Fearless-Concurrency.md +++ b/_posts/2015-04-10-Fearless-Concurrency.md @@ -8,7 +8,7 @@ description: "Rust's vision for concurrency" The Rust project was initiated to solve two thorny problems: * How do you do safe systems programming? -* How do you do make concurrency painless? +* How do you make concurrency painless? Initially these problems seemed orthogonal, but to our amazement, the solution turned out to be identical: **the same tools that make Rust From 662fbcfb4a605cc879bb077e10d402001c47a347 Mon Sep 17 00:00:00 2001 From: Will Hipschman Date: Fri, 10 Apr 2015 17:41:52 -0700 Subject: [PATCH 076/147] Fearless Concurrency Typo This 's' was lost, but now it's home again. --- _posts/2015-04-10-Fearless-Concurrency.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_posts/2015-04-10-Fearless-Concurrency.md b/_posts/2015-04-10-Fearless-Concurrency.md index 4e7635eae..29f10090a 100644 --- a/_posts/2015-04-10-Fearless-Concurrency.md +++ b/_posts/2015-04-10-Fearless-Concurrency.md @@ -490,7 +490,7 @@ fn parent() { } ~~~~ -The child thread take a reference to `vec`, which in turn resides in +The child thread takes a reference to `vec`, which in turn resides in the stack frame of `parent`. When `parent` exits, the stack frame is popped, but the child thread is none the wiser. Oops! From d73904ee399cb5cec7ee215b2df5117aacb8d6e0 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Wed, 8 Apr 2015 18:24:35 +0200 Subject: [PATCH 077/147] first draft of enums post. --- ...15-04-17-Enums-match-mutation-and-moves.md | 734 ++++++++++++++++++ 1 file changed, 734 insertions(+) create mode 100644 _posts/2015-04-17-Enums-match-mutation-and-moves.md diff --git a/_posts/2015-04-17-Enums-match-mutation-and-moves.md b/_posts/2015-04-17-Enums-match-mutation-and-moves.md new file mode 100644 index 000000000..2ee059374 --- /dev/null +++ b/_posts/2015-04-17-Enums-match-mutation-and-moves.md @@ -0,0 +1,734 @@ +--- +layout: post +title: "Enums: match, mutation, and moves" +author: Felix S. Klock II +description: "A tour of enums in Rust." +--- + +In this post we explore one corner of the Rust language: defining a +class of data via `enum`, and processing instances of such data via +`match`. + +Most of the post will use a single running example: modeling a game of +chess. At the end the post changes gears, shifting to a discussion of +how affine-typing ensures that the demonstrated features do not +introduce unsoundness. + +## Tutorial material + +The post takes the overall form of a tutorial. + +If you are already familiar with defining `enums` and simple uses of +`match`, you may want to skip ahead to the [mutating enums] or +[avoiding unsoundness] sections. This first section is meant to +establish everything one would need to know about enums to understand +the discussion in latter two sections. + +[mutating enums]: #mutating-enums +[avoiding unsoundness]: #avoiding-unsoundness + +### Defining simple enums + +The pieces of chess can be classified as follows: + +```rust +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum PieceShape { + King, Queen, Rook, Bishop, Knight, Pawn, +} +``` + +The `derive` line automatically generates support for three common +operations: the `Copy, Clone` gives us the ability to freely copy or +clone instances of `PieceShape`, the `Debug` provides support for +printing each piece-rank into an output buffer, e.g. via +`println!("piece: {:?}", piece)`, and `PartialEq, Eq` provides support +for the `==` operator on instances of the enum. + +### Enum discriminant values + +An enum like `PieceShape` above, where each variant is just a name, is +represented using an integer value for each variant. The definition +style above allows the Rust compiler to assign the values itself; it +will start from 0 and count upwards. + +Alternatively, one can manually assign specific values to enum +variants, interrupting the automatic value assignment performed by the +compiler. Thus, we could instead have written the below, and get the +same assignment of discriminant values: + +```rust +#[cfg(alternative)] +#[derive(Copy, Clone, Debug)] +pub enum PieceShape { + Pawn = 5, Rook = 2, Bishop, Knight, King = 0, Queen, +} +``` + +These enums, where each variant is just a name with an associated +(implicitly- or explicitly-assigned) integer value, are known as +"C-style enums" in Rust parlance, since the syntax largely matches +that used for the `enum` construct in the C language. + +(However, note that not every C enum immediately corresponds to an +analogous enum in Rust; in particular, Rust enforces an invariant that +each variant be distinct from all other variants in the enum, while C +allows one to assign the same integer value to multiple enum +variants.) + +### Constructing enum instances + +After defining an enum, one accesses the variants it defines via the +path syntax `enum_name::variant_name`. So using these pieces is +straight-forward enough: + +```rust +#[test] +fn demo_debug_format() { + let q = PieceShape::Queen; + let p = PieceShape::Pawn; + let k = PieceShape::King; + println!("q={:?} p={:?} k={:?}", q, p, k); +} +``` + +Of course, it can be annoying to have to type the enum name +repeatedly. To make the variants directly accessibly, import +them via `use`. + +```rust +#[test] +fn demo_debug_format_2() { + use self::PieceShape::{Queen, Pawn, King}; + let q = Queen; + let p = Pawn; + let k = King; + println!("q={:?} p={:?} k={:?}", q, p, k); +} +``` + +or even more simply: + +```rust +use self::PieceShape::*; +``` + +(Note that we have used `use self::...` here; the `use`-import syntax +resolves paths relative to the crate-root by default, but the leading +`self` changes the mode so that the resolution is relative to the +containing module.) + +### Destructuring enums via `match` + +Once we have created enum instances, we can process them via `match` +expressions. Here, we map each piece rank to a single letter code. + +```rust +pub fn one_letter(r: PieceShape) -> char { + match r { + Pawn => 'P', + Rook => 'R', + Knight => 'N', + Bishop => 'B', + Queen => 'Q', + King => 'K', + } +} + +#[test] +fn demo_one_letter() { + assert_eq!(one_letter(Queen), 'Q'); +} +``` + +Or as a more complex example, we can gather a sequence of shapes into a +character string: + +```rust +#[test] +fn demo_one_letter_collect() { + let qpk = [Queen, Pawn, King]; + let qpk: String = qpk.iter().map(|r|{one_letter(*r)}).collect(); + assert_eq!(qpk, "QPK"); +} +``` + +Encoding shapes via ASCII characters has worked fine since the 1960's, +but since we live in a more modern age, it is tempting to find out +whether we could encode our pieces with Unicode pictorial symbols. + +Indeed, reviewing the Unicode character charts, we discover that the +chess pieces have dedicated characters, but we see that there are +twelve such characters (starting from `U+2654`, "WHITE CHESS KING"), +not six. This reveals an oversight in our data description thus far: +We have represented shapes, but not the *color* of the piece. + +### Enums with payloads + +Here is one way of many to add color to our pieces, via a new `enum` +which represents one square of a chess board. + +```rust +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +enum Piece { + Black(PieceShape), + White(PieceShape), +} +``` + +This demonstrates a twist on `enum` that Rust provides. +Unlike an `enum` in C, which can only define a collection of names, +each variant of a Rust enum can optionally carry a payload of data. + +### Matching tree-structured patterns + +With this in hand, we can render a piece in a manner similar to how we +converted a shape to a letter in `one_letter` above. + +```rust +fn render(p: Piece) -> char { + match p { + Piece::White(King) => '\u{2654}', + Piece::White(Queen) => '\u{2655}', + Piece::White(Rook) => '\u{2656}', + Piece::White(Bishop) => '\u{2657}', + Piece::White(Knight) => '\u{2658}', + Piece::White(Pawn) => '\u{2659}', + + Piece::Black(King) => '\u{265A}', + Piece::Black(Queen) => '\u{265B}', + Piece::Black(Rook) => '\u{265C}', + Piece::Black(Bishop) => '\u{265D}', + Piece::Black(Knight) => '\u{265E}', + Piece::Black(Pawn) => '\u{265F}', + } +} + +#[test] +fn demo_render() { + let row = [Rook, Knight, Bishop, Queen, King, Bishop, Knight, Rook]; + let black_row: String = row.iter().map(|s| render(Piece::Black(*s))).collect(); + let white_row: String = row.iter().map(|s| render(Piece::White(*s))).collect(); + println!("black_row: {}", black_row); + println!("white_row: {}", white_row); +} +``` + +The `demo_render` test prints: + +``` +black_row: ♜♞♝♛♚♝♞♜ +white_row: ♖♘♗♕♔♗♘♖ +``` + +which is well on its way to a fully-rendered chess board. + +### Binding in match patterns + +Here is an alternative way to write the `render` function. + +```rust +fn render_2(p: Piece) -> char { + use std::char; + let offset = match p { + Piece::White(shape) => shape as u32, + Piece::Black(shape) => shape as u32, + }; + let king_unicode_value = match p { + Piece::White(_) => 0x2654, + Piece::Black(_) => 0x265A, + }; + char::from_u32(king_unicode_value + offset).unwrap() +} + +#[test] +fn check_renders() { + for &r in &[King, Queen, Rook, Bishop, Knight, Pawn] { + assert_eq!(render(Piece::White(r)), + render_2(Piece::White(r))); + assert_eq!(render(Piece::Black(r)), + render_2(Piece::Black(r))); + } +} +``` + +The above code illustrates a collection of features: + + * Rather than matching it as constant, one can bind the payload + attached to a enum-variant to an identifier. Thus the patterns in + the first `match` expression are each binding `shape` to the + corrresponding shape-payload attached to the piece. + + * One can cast a C-style enum value to the integer value used to + represent it, via the syntax `enum_value as integer_type`. + + * One can use `_` to ignore substructure in a pattern. This is used + in the second match expression, so that the shape held in `p` is + left in place. (This does not matter too much since `Shape` + implements `Copy`; it is often more important in cases where the + payload is not copyable, as further discussed below.) + +Thus, the `White` and `Black` `match`-clauses in the above code bind +`shape` to the associated `PieceShape`, and then casts that shape to +the appropriate integer offset from the king in the unicode codepoint +assignment. (The integers in our original enum definition for +`PieceShape` were selected so that they would match the sequence of +piece ranks in the unicode codepoint assignment.) + +## Mutating enums + +One event that occurs in chess is that when a pawn reaches the eighth +rank of the board, it is promoted to a another piece of the same color +(usually a queen). + +It is easy to write such a promotion in a functional programming +style: + +```rust +fn promote_to_queen_functional(p: Piece) -> Piece { + match p { + Piece::Black(_) => Piece::Black(Queen), + Piece::White(_) => Piece::White(Queen), + } +} + +#[test] +fn demo_promote_functional() { + let p = Piece::White(Pawn); + assert_eq!(promote_to_queen_functional(p), Piece::White(Queen)); +} +``` + +Let us imagine, however, that we want to model an actual chess board, +and we want to implement promotion as an *in-place* modification; that +is, instead of constructing a whole new copy of the board, we want to +instead use an imperative update to represent the promotion. + +### A first bumbling attempt + +Here is a first, admittedly bumbling, attempt to implement in-place +promotion from a pawn to a queen: It just removes the `-> Piece` +return type and shoehorns an assignment into the code, in the hopes +that everything will work out. + +So, first try: + +```rust +#[cfg(promote_attempt_1)] +fn promote_to_queen_1(mut pawn: Piece) { + pawn = match pawn { + Piece::Black(_) => Piece::Black(Queen), + Piece::White(_) => Piece::White(Queen), + }; +} + +#[cfg(promote_attempt_1)] +#[test] +fn demo_promote_1() { + let p = Piece::White(Pawn); + assert_eq!(p, Piece::White(Pawn)); + promote_to_queen_1(p); + assert_eq!(p, Piece::White(Queen)); +} +``` + +Compiling this (enabling it via `--cfg promote_attempt_1`) yields: + +``` +enums.rs:238:5: 238:9 warning: value assigned to `pawn` is never read, #[warn(unused_assignments)] on by default +enums.rs:238 pawn = match pawn { + ^~~~ +``` + +So that's not a good sign. Then running it yields: + +``` +test demo_promote_1 ... FAILED + +failures: + +---- demo_promote_1 stdout ---- +thread 'demo_promote_1' panicked at 'assertion failed: `(left == right)` (left: `White(Pawn)`, right: `White(Queen)`)', enums.rs:250 +``` + +This demonstrates, among other things, that static analysis cannot +stop someone who is determined to write buggy code. But the assertion +failure message is fairly clear about what the problem is: We had +hoped the piece `p` would be replaced with a queen, but it is in fact +still a pawn. + +The crucial mistake being made here is that if you want to mutate a +piece of state in place, then you need to have a mutable *reference* +to that state. The `mut pawn` formal argument to `promote_to_queen` is +indeed mutable, but those mutations are not visible from outside the +function, because the argument has already been moved into the +parameter's location. The invocation `promote_to_queen` is not passing +a reference (mutable or otherwise) to `p`; it is creating a *copy* of +the `Piece`. + +### A second stumble, caught at compile-time + +To make this really concrete, let us consider another kind of data in +our chess board. A chess board is made up of squares, usually +arranged in an 8-by-8 grid. Each `Square` is either unoccupied +(`Empty`) or is occupied by a single piece. + +```rust +#[derive(Clone, Debug, PartialEq, Eq)] +enum Square { + Empty, + Occupied(Piece), +} +``` + +Note that we have left off the `Copy` derivation; the intention is +that while `Rank` and `Piece` can be freely copied via assignment +statements, the square on a chess board has its own identity, and +should not be freely copied via assignment statements. + +With that in mind, let us try writing `promote_to_queen` again, but +this time passing in a `Square`. + +(This example is also taking advantage of another feature of Rust's +`match` syntax: one can combine multiple match arms that have +identical code into one arm by writing all their patterns pairwise +separated by a vertical bar (`|`)): + + +```rust +#[cfg(promote_attempt_2)] +fn promote_to_queen_2(mut s: Square) { + match s { + Square::Empty => {} + Square::Occupied(Piece::White(mut shape)) | + Square::Occupied(Piece::Black(mut shape)) => { + shape = Queen + } + } +} + +#[cfg(promote_attempt_2)] +#[test] +fn demo_promote_2() { + let mut s = Square::Occupied(Piece::White(Pawn)); + promote_to_queen_2(s); + assert_eq!(s, Square::Occupied(Piece::White(Queen))); +} +``` + +This second attempt has the same mistake that we made with +`promote_to_queen_2`: we again have failed to take a mutable reference +to the square, so no in-place update is possible. But *now* we get a +compile-time error: + +``` +:3:11: 3:23 error: use of moved value: `s` +:3 match ( & ( $ left ) , & ( $ right ) ) { + ^~~~~~~~~~~~ +:1:1: 9:39 note: in expansion of assert_eq! +enums.rs:319:5: 319:58 note: expansion site +enums.rs:318:24: 318:25 note: `s` moved here because it has type `Square`, which is non-copyable +enums.rs:318 promote_to_queen_2(s); + ^ +``` + +The error message is telling us exactly what happened: the argument to +`promote_to_queen_2` is moved when the function is called; thus the +attempt to reference `s` in the `assert_eq!` invocation fails, because +`s` has been moved. + +(This illustrates why one might *not* add `derive(Copy)` to every data +type where it would otherwise be legal; leaving out `derive(Copy)` can +catch certain bugs.) + +### Try, try again; taking a reference + +So, here's a third try, where we take a reference to the `Square`. + +```rust +#[cfg(promote_attempt_3)] +fn promote_to_queen_3(s: &mut Square) { + match s { + &mut Square::Empty => {} + &mut Square::Occupied(Piece::White(mut shape)) | + &mut Square::Occupied(Piece::Black(mut shape)) => { + shape = Queen + } + } +} + +#[cfg(promote_attempt_3)] +#[test] +fn demo_promote_3() { + let mut p = Square::Occupied(Piece::White(Pawn)); + promote_to_queen_3(&mut p); + assert_eq!(p, Square::Occupied(Piece::White(Queen))); +} +``` + +We again see the warning: + +``` +enums.rs:355:44: 355:49 warning: variable `shape` is assigned to, but never used, #[warn(unused_variables)] on by default +enums.rs:355 &mut Square::Occupied(Piece::White(mut shape)) | + ^~~~~~~~~ +enums.rs:357:13: 357:14 warning: value assigned to `shape` is never read, #[warn(unused_assignments)] on by default +enums.rs:357 shape = Queen + ^~~~~ +``` + +This warning is again warranted, as the test fails: + +``` +test demo_promote_3 ... FAILED + +failures: + +---- demo_promote_3 stdout ---- +thread 'demo_promote_3' panicked at 'assertion failed: `(left == right)` (left: `Occupied(White(Pawn))`, right: `Occupied(White(Queen))`)', enums.rs:367 +``` + +### Keeping `ref`s into referenced data + +So, what happened here? + +The answer is: Even though we have ensured that `Square` is not +accidentally copied, there is nothing stopping the *contents* of +`Square` from being copied. So in a `match` arm like +``` +&mut Square::Occupied(Piece::Black(mut shape)) => { ... } +``` +if the discriminant value matches that arm, then it will put a +copy of its shape into `shape`. + +This brings us to a very important point about how `match` works in +Rust: it matches its inputs in-place, but normal bindings like the +`mut shape` above will move (or copy) the matched substructure into +fresh locations. + +If you want to capture a reference into the matched substructure, +either to avoid extraneous copying, or to enable modification of the +original input value, then you need to use a `ref`-pattern. + +With this knowledge in hand, let us try again. + +In addition, our fourth try simplifies the code slightly by +dereferencing the argument `*s`. + +```rust +fn promote_to_queen_4(s: &mut Square) { + match *s { + Square::Empty => {} + Square::Occupied(Piece::White(ref mut shape)) | + Square::Occupied(Piece::Black(ref mut shape)) => { + *shape = Queen + } + } +} + +#[test] +fn demo_promote_4() { + let mut p = Square::Occupied(Piece::White(Pawn)); + promote_to_queen_4(&mut p); + assert_eq!(p, Square::Occupied(Piece::White(Queen))); +} +``` + +Compiling and running our code now yields: + +``` +running 7 tests +test demo_debug_format ... ok +test demo_one_letter_collect ... ok +test demo_one_letter ... ok +test demo_debug_format_2 ... ok +test demo_promote_4 ... ok +test check_renders ... ok +test demo_promote_functional ... ok + +test result: ok. 7 passed; 0 failed; 0 ignored; 0 measured +``` + +Huzzah, it works! + +The dereference `*s` works because `match` works on both "L-value" and +"R-value" expressions; L-value expressions evaluate to memory +locations, while R-value expressions evaluate to values that are then +stored, if necessary, in temporary memory locations. It simplified our +code because allowed us to sidestep writing `&mut` at the beginning of +each of our patterns, which was not such a great burden for this tiny +code snippet, but can be much more annoying when working with `match` +expressions that have many more arms, amplifying the redundancy. + +## Avoiding unsoundness + +(This section is adapted from the Rust demo we presented at the 2014 +ML workshop.) + +A potentially non-obvious issue arises when a supposedly-sound +language adds support for `ref mut`, or even just `ref`, in a +non-moving/non-copying `match` expression: How does one prevent +someone from injecting unsound behavior by using mutable references to +overwrite state secretly, subverting the type-system? + +Consider the following code: + +```rust +#[test] +fn sound_code() { + enum E { A(fn (i8) -> i8), B(usize) } + fn add3(x:i8) -> i8 { x + 3 } + let mut a = E::A(add3); let mut b = E::B(19); + { + let m1 = &mut a; let m2 = &mut b; + foo(m1, m2); + } + match b { + E::A(_fn) => println!("b is an E::A"), + E::B(val) => println!("b is an E::B: 0x{:x}", val), + } + + fn foo(p1: &mut E, p2: &mut E) { + match p1 { + &mut E::B(..) => panic!("cannot happen"), + &mut E::A(ref adder) => { + /* WATCH: */ *p2 = E::B(0xBadC0de); + println!("{}", (*adder)(14)); + } + } + } +} +``` + +The above compiles and runs just fine: the references to `a` and `b` +are copied to `p1` and `p2` respectively for the call to `foo`, `b` is +imperatively updated via `p2`, `foo` prints out `17` (= 14 + 3), and +then after `foo` returns it prints out `b is an E::B: 0xbadc0de`. + +The question is: what happens when we tweak the test slightly? + +### Attempted subversion via local overwrites + +For example, what if we imperatively modify `*p1` instead of `*p2`, +but leave the code otherwise unchanged? + +Storing the `E::B(0xBadCode)` would invalidate the data held in +`adder` (indeed, it would put "bad code" there), and so the subsequent +invocation of `adder` will jump to some (potentially invalid) memory +location and try to interpret it as executable code. + +But watch: + +```rust +#[cfg(wont_compile)] +#[test] +fn unsound_code_1() { + enum E { A(fn (i8) -> i8), B(usize) } + fn add3(x:i8) -> i8 { x + 3 } + let mut a = E::A(add3); let mut b = E::B(19); + { + let m1 = &mut a; let m2 = &mut b; + foo(m1, m2); + } + match b { + E::A(_fn) => println!("b is an E::A"), + E::B(val) => println!("b is an E::B: 0x{:x}", val), + } + + fn foo(p1: &mut E, p2: &mut E) { + match p1 { + &mut E::B(..) => panic!("cannot happen"), + &mut E::A(ref adder) => { + /* was p2 */ *p1 = E::B(0xBadC0de); + println!("{}", (*adder)(14)); + } + } + } +} +``` + +As you might have inferred from the `cfg` annotation above, this +will not compile. Attempting to compile it yields: + +``` +enums.rs:569:30: 569:51 error: cannot assign to `*p1` because it is borrowed +enums.rs:569 /* was p2 */ *p1 = E::B(0xBadC0de); + ^~~~~~~~~~~~~~~~~~~~~ +enums.rs:568:23: 568:32 note: borrow of `*p1` occurs here +enums.rs:568 &mut E::A(ref adder) => { + ^~~~~~~~~ +``` + +So, the compiler prevents us from overwriting the function pointer +with bogus data! (Of course, if you *really* want to shoot yourself in +the foot, you can resort to `unsafe` code to sneak the overwrite +through; at that point you are explicitly side-stepping the type +system, which Rust allows.) + +### Attempted subversion via aliasing + +Furthermore, the same rules that prevented the last example from +compiling are not performing a local check of just the single function +`foo`; they are enforcing a *global* soundness property. Other +non-local attempts to subvert the type system by sneaking the +overwrite past the static checks are foiled, as shown here: + +```rust +#[cfg(wont_compile)] +#[test] +fn unsound_code_2() { + enum E { A(fn (i8) -> i8), B(usize) } + fn add3(x:i8) -> i8 { x + 3 } + let mut a = E::A(add3); let mut b = E::B(19); + { + let m1 = &mut a; let m2 = &mut b; + foo(m1, m1); + } + match b { + E::A(_fn) => println!("b is an E::A"), + E::B(val) => println!("b is an E::B: 0x{:x}", val), + } + + fn foo(p1: &mut E, p2: &mut E) { + match p1 { + &mut E::B(..) => panic!("cannot happen"), + &mut E::A(ref adder) => { + /* watch? */ *p2 = E::B(0xBadC0de); + println!("{}", (*adder)(14)); + } + } + } +} +``` + +The above is attempting to create an *alias* between `p1` and `p2`, +thus causing the write to `p2` to actually overwrite the data +we matched in `p1`. + +However, this example also fails to compile: + +``` +enums.rs:612:17: 612:19 error: cannot borrow `*m1` as mutable more than once at a time +enums.rs:612 foo(m1, m1); + ^~ +enums.rs:612:13: 612:15 note: previous borrow of `*m1` occurs here; the mutable borrow prevents subsequent moves, borrows, or modification of `*m1` until the borrow ends +enums.rs:612 foo(m1, m1); + ^~ +enums.rs:612:20: 612:20 note: previous borrow ends here +enums.rs:612 foo(m1, m1); + ^ +``` + +This illustrates why Rust takes such pains to support affine typing of +`&mut T` and other owned types: It is necessary for type soundness! + +## Conclusion + +Thus ends our tour of enums in Rust. For more information on details +that were not covered here, such as binding via `ident @ pattern`, or +the potentially subtle difference between `{ let id = expr; ... }` +versus `match expr { id => { ... } }`, consult the Rust documentation, +or quiz our awesome community (in `#rust` on IRC, or in the +[user group]). + +[user group]: http://users.rust-lang.org/ From cd513f361bcfdc24d18d9340b8041d67d0a94129 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Thu, 9 Apr 2015 15:15:59 +0200 Subject: [PATCH 078/147] Added note about the `cfg(alternative)`. --- _posts/2015-04-17-Enums-match-mutation-and-moves.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/_posts/2015-04-17-Enums-match-mutation-and-moves.md b/_posts/2015-04-17-Enums-match-mutation-and-moves.md index 2ee059374..6aa5ba810 100644 --- a/_posts/2015-04-17-Enums-match-mutation-and-moves.md +++ b/_posts/2015-04-17-Enums-match-mutation-and-moves.md @@ -65,6 +65,12 @@ pub enum PieceShape { } ``` +(In the above, the `cfg` line is how one denotes "conditional +compilation" in Rust; it indicates that the `enum` definition beneath +it should only be compiled if one passes `--cfg alternative` to the +invocation of `rustc`; it is being used above as a quick-and-dirty way +to avoid causing a compile-time error with a duplicate definition.) + These enums, where each variant is just a name with an associated (implicitly- or explicitly-assigned) integer value, are known as "C-style enums" in Rust parlance, since the syntax largely matches From 5a2f10c070c23fa63925e000f379f6c4132e5452 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Wed, 15 Apr 2015 16:47:14 +0200 Subject: [PATCH 079/147] second draft. near complete rewrite. --- ...15-04-17-Enums-match-mutation-and-moves.md | 877 +++++++++--------- 1 file changed, 413 insertions(+), 464 deletions(-) diff --git a/_posts/2015-04-17-Enums-match-mutation-and-moves.md b/_posts/2015-04-17-Enums-match-mutation-and-moves.md index 6aa5ba810..318d36754 100644 --- a/_posts/2015-04-17-Enums-match-mutation-and-moves.md +++ b/_posts/2015-04-17-Enums-match-mutation-and-moves.md @@ -1,572 +1,520 @@ --- layout: post -title: "Enums: match, mutation, and moves" +title: "Mixing matching, mutation, and moves in Rust" author: Felix S. Klock II -description: "A tour of enums in Rust." +description: "A tour of matching and enums in Rust." --- -In this post we explore one corner of the Rust language: defining a -class of data via `enum`, and processing instances of such data via -`match`. +One of the primary goals of the Rust project is to enable safe systems +programming. Systems programming usually implies imperative +programming, which in turns often implies side-effects, reasoning +about shared, aliasable state, et cetera. -Most of the post will use a single running example: modeling a game of -chess. At the end the post changes gears, shifting to a discussion of -how affine-typing ensures that the demonstrated features do not -introduce unsoundness. +At the same time, to provide *safety*, Rust programs and data types +must be structured in a way that allows static checking to ensure +soundness. Rust has features and restrictions that operate in tandem +to ease writing programs that can pass these checks and thus ensure +safety. For example, Rust incorporates the notion of *ownership* deeply +into the language. -## Tutorial material +Rust's `match` expression is a construct that offers an interesting +combination of such features and restrictions. A `match` expression +takes an input value, classifies it, and then jumps to code written to +handle the identified class of data. -The post takes the overall form of a tutorial. +In this post we explore how Rust processes such data via `match`. +The crucial elements that `match` and its counterpart `enum` tie +together are: -If you are already familiar with defining `enums` and simple uses of -`match`, you may want to skip ahead to the [mutating enums] or -[avoiding unsoundness] sections. This first section is meant to -establish everything one would need to know about enums to understand -the discussion in latter two sections. +* Exhaustive case analysis, which ensures that no case is omitted + when processing an input. -[mutating enums]: #mutating-enums -[avoiding unsoundness]: #avoiding-unsoundness +* `match` embraces both imperative and applicative styles of + programming. The compiler's static analyses work hard to ensure + statement-oriented programming remains palatable, leaving the + question of whether expression-orientation is better to style + guides. -### Defining simple enums +* Destructuring bind of *L-values*: Rust encourages the developer to + think carefully about ownership and borrowing. To ensure that + processing data does not force one to give up ownership of a value + prematurely, `match` is designed with support for merely *borrowing* + substructure within its input (as opposed to always *moving* such + substructure). -The pieces of chess can be classified as follows: +We cover each of the items above in detail below, but first we +establish a foundation for the discussion: What does `match` look +like, and how does it work? -```rust -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum PieceShape { - King, Queen, Rook, Bishop, Knight, Pawn, -} -``` - -The `derive` line automatically generates support for three common -operations: the `Copy, Clone` gives us the ability to freely copy or -clone instances of `PieceShape`, the `Debug` provides support for -printing each piece-rank into an output buffer, e.g. via -`println!("piece: {:?}", piece)`, and `PartialEq, Eq` provides support -for the `==` operator on instances of the enum. +### The Basics of `match` -### Enum discriminant values - -An enum like `PieceShape` above, where each variant is just a name, is -represented using an integer value for each variant. The definition -style above allows the Rust compiler to assign the values itself; it -will start from 0 and count upwards. - -Alternatively, one can manually assign specific values to enum -variants, interrupting the automatic value assignment performed by the -compiler. Thus, we could instead have written the below, and get the -same assignment of discriminant values: +The `match` expression in Rust has this form: ```rust -#[cfg(alternative)] -#[derive(Copy, Clone, Debug)] -pub enum PieceShape { - Pawn = 5, Rook = 2, Bishop, Knight, King = 0, Queen, +match INPUT_EXPRESSION { + PREDICATE_1 => RESULT_EXPRESSION_1, + PREDICATE_2 => RESULT_EXPRESSION_2, + ... + PREDICATE_n => RESULT_EXPRESSION_n } ``` -(In the above, the `cfg` line is how one denotes "conditional -compilation" in Rust; it indicates that the `enum` definition beneath -it should only be compiled if one passes `--cfg alternative` to the -invocation of `rustc`; it is being used above as a quick-and-dirty way -to avoid causing a compile-time error with a duplicate definition.) +where each of the `PREDICATE_i` contains at least one *pattern*. A +pattern describes a subset of the possible values to which +`INPUT_EXPRESSION` could evaluate. +The syntax `PREDICATE => RESULT_EXPRESSION` is called a "match arm", +or simply "arm". -These enums, where each variant is just a name with an associated -(implicitly- or explicitly-assigned) integer value, are known as -"C-style enums" in Rust parlance, since the syntax largely matches -that used for the `enum` construct in the C language. +Patterns can match atomic values, like integers or characters; they +can also match user-defined symbolic data, defined via `enum`. -(However, note that not every C enum immediately corresponds to an -analogous enum in Rust; in particular, Rust enforces an invariant that -each variant be distinct from all other variants in the enum, while C -allows one to assign the same integer value to multiple enum -variants.) +The below code demonstrates generating the next guess (poorly) in a number +guessing game, given the answer from a previous guess. -### Constructing enum instances +(Incidentally, nearly all the code in this post is directly +executable; you can cut-and-paste the code snippets into a file +`demo.rs`, compile the file with `--test`, and run the resulting +binary to see the tests run.) -After defining an enum, one accesses the variants it defines via the -path syntax `enum_name::variant_name`. So using these pieces is -straight-forward enough: - -```rust -#[test] -fn demo_debug_format() { - let q = PieceShape::Queen; - let p = PieceShape::Pawn; - let k = PieceShape::King; - println!("q={:?} p={:?} k={:?}", q, p, k); +```rust,code +enum Answer { + Higher, + Lower, + Bingo, } -``` -Of course, it can be annoying to have to type the enum name -repeatedly. To make the variants directly accessibly, import -them via `use`. +fn suggest_guess(prior_guess: u32, answer: Answer) { + match answer { + Answer::Higher => println!("maybe try {} next", prior_guess + 10), + Answer::Lower => println!("maybe try {} next", prior_guess - 1), + Answer::Bingo => println!("we won with {}!", prior_guess), + } +} -```rust #[test] -fn demo_debug_format_2() { - use self::PieceShape::{Queen, Pawn, King}; - let q = Queen; - let p = Pawn; - let k = King; - println!("q={:?} p={:?} k={:?}", q, p, k); +fn demo_suggest_guess() { + suggest_guess(10, Answer::Higher); + suggest_guess(20, Answer::Lower); + suggest_guess(19, Answer::Bingo); } ``` -or even more simply: - -```rust -use self::PieceShape::*; -``` +Patterns can also match structured data (e.g. tuples, slices, user-defined +data types) via corresponding patterns. In such patterns, one often +binds substructure of the input to local variables (identifer patterns), +for use either in the arm's predicate or in its result. -(Note that we have used `use self::...` here; the `use`-import syntax -resolves paths relative to the crate-root by default, but the leading -`self` changes the mode so that the resolution is relative to the -containing module.) +The special `_` pattern matches any single value, and is often used as +a catch-all; the special `..` pattern generalizes this by matching any +*series* of values or name/value pairs. -### Destructuring enums via `match` +Also, one can collapse multiple patterns into one arm by separating the +patterns by vertical bars (`|`); thus that arm matches either this pattern, +or that pattern, et cetera. -Once we have created enum instances, we can process them via `match` -expressions. Here, we map each piece rank to a single letter code. +These features are illustrated in the following revision to the +guessing-game answer generation strategy: -```rust -pub fn one_letter(r: PieceShape) -> char { - match r { - Pawn => 'P', - Rook => 'R', - Knight => 'N', - Bishop => 'B', - Queen => 'Q', - King => 'K', - } +```rust,code +struct GuessState { + guess: u32, + answer: Answer, + low: u32, + high: u32, } -#[test] -fn demo_one_letter() { - assert_eq!(one_letter(Queen), 'Q'); +fn suggest_guess_smarter(s: GuessState) { + match s { + GuessState { answer: Answer::Bingo, guess: p, .. } => { + println!("we won with {}!", p); + } + GuessState { answer: Answer::Higher, guess: l, low: _, high: h } | + GuessState { answer: Answer::Lower, guess: h, low: l, high: _ } => { + let mid = l + ((h - l) / 2); + println!("lets try {} next", mid); + } + } } -``` - -Or as a more complex example, we can gather a sequence of shapes into a -character string: -```rust #[test] -fn demo_one_letter_collect() { - let qpk = [Queen, Pawn, King]; - let qpk: String = qpk.iter().map(|r|{one_letter(*r)}).collect(); - assert_eq!(qpk, "QPK"); +fn demo_guess_state() { + suggest_guess_smarter(GuessState { + guess: 20, answer: Answer::Lower, low: 10, high: 1000 + }); } ``` -Encoding shapes via ASCII characters has worked fine since the 1960's, -but since we live in a more modern age, it is tempting to find out -whether we could encode our pieces with Unicode pictorial symbols. - -Indeed, reviewing the Unicode character charts, we discover that the -chess pieces have dedicated characters, but we see that there are -twelve such characters (starting from `U+2654`, "WHITE CHESS KING"), -not six. This reveals an oversight in our data description thus far: -We have represented shapes, but not the *color* of the piece. +That is `match` in a nutshell. -### Enums with payloads +So, what is the interplay between this construct and Rust's approach to +ownership and safety in general? -Here is one way of many to add color to our pieces, via a new `enum` -which represents one square of a chess board. +### Exhaustive case analysis -```rust -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -enum Piece { - Black(PieceShape), - White(PieceShape), -} -``` +One important method of analytical thinking is case analysis: Dividing +a problem into some number of separate cases, and then analyzing each +case individually. -This demonstrates a twist on `enum` that Rust provides. -Unlike an `enum` in C, which can only define a collection of names, -each variant of a Rust enum can optionally carry a payload of data. +For this method of problem solving to work, the cases must be +*collectively exhaustive*; otherwise, a case that was not covered +would mean a potential problem instance for which no solution has been +identified. -### Matching tree-structured patterns +This brings us to one of the fundamental restrictions of Rust's +`match` construct: the collection of provided cases must be exhautive. -With this in hand, we can render a piece in a manner similar to how we -converted a shape to a letter in `one_letter` above. +So, for example, the following code is rejected at compile-time. ```rust -fn render(p: Piece) -> char { - match p { - Piece::White(King) => '\u{2654}', - Piece::White(Queen) => '\u{2655}', - Piece::White(Rook) => '\u{2656}', - Piece::White(Bishop) => '\u{2657}', - Piece::White(Knight) => '\u{2658}', - Piece::White(Pawn) => '\u{2659}', - - Piece::Black(King) => '\u{265A}', - Piece::Black(Queen) => '\u{265B}', - Piece::Black(Rook) => '\u{265C}', - Piece::Black(Bishop) => '\u{265D}', - Piece::Black(Knight) => '\u{265E}', - Piece::Black(Pawn) => '\u{265F}', - } -} - -#[test] -fn demo_render() { - let row = [Rook, Knight, Bishop, Queen, King, Bishop, Knight, Rook]; - let black_row: String = row.iter().map(|s| render(Piece::Black(*s))).collect(); - let white_row: String = row.iter().map(|s| render(Piece::White(*s))).collect(); - println!("black_row: {}", black_row); - println!("white_row: {}", white_row); +fn suggest_guess_broken(prior_guess: u32, answer: Answer) { + let next_guess = match answer { + Answer::Higher => prior_guess + 10, + Answer::Lower => prior_guess - 1, + // ERROR: non-exhaustive patterns: `Bingo` not covered + }; + println!("maybe try {} next", next_guess); } ``` -The `demo_render` test prints: - -``` -black_row: ♜♞♝♛♚♝♞♜ -white_row: ♖♘♗♕♔♗♘♖ -``` +Many other languages offer a pattern matching construct (ML and +various macro-based `match` implementations in Scheme both come to +mind), but not all of them have this restriction. -which is well on its way to a fully-rendered chess board. +Rust has this restriction for two reasons: -### Binding in match patterns +* First, as noted above, dividing a problem into cases only yields a +general solution if the cases are exhaustive. Exhaustiveness-checking +exposes logical errors. -Here is an alternative way to write the `render` function. +* Second, since `match` is an expression form, exhaustiveness ensures +that such expressions always evaluates to a value of the correct type +(or jump elsehwere in the program, as illustrated here): -```rust -fn render_2(p: Piece) -> char { - use std::char; - let offset = match p { - Piece::White(shape) => shape as u32, - Piece::Black(shape) => shape as u32, - }; - let king_unicode_value = match p { - Piece::White(_) => 0x2654, - Piece::Black(_) => 0x265A, +```rust,code +fn suggest_guess_fixed(prior_guess: u32, answer: Answer) { + let next_guess = match answer { + Answer::Higher => prior_guess + 10, + Answer::Lower => prior_guess - 1, + Answer::Bingo => { + println!("we won!"); + return; + } }; - char::from_u32(king_unicode_value + offset).unwrap() + println!("maybe try {} next", next_guess); } #[test] -fn check_renders() { - for &r in &[King, Queen, Rook, Bishop, Knight, Pawn] { - assert_eq!(render(Piece::White(r)), - render_2(Piece::White(r))); - assert_eq!(render(Piece::Black(r)), - render_2(Piece::Black(r))); - } +fn demo_guess_fixed() { + suggest_guess_fixed(10, Answer::Higher); + suggest_guess_fixed(20, Answer::Lower); + suggest_guess_fixed(19, Answer::Bingo); } ``` -The above code illustrates a collection of features: - - * Rather than matching it as constant, one can bind the payload - attached to a enum-variant to an identifier. Thus the patterns in - the first `match` expression are each binding `shape` to the - corrresponding shape-payload attached to the piece. - - * One can cast a C-style enum value to the integer value used to - represent it, via the syntax `enum_value as integer_type`. - - * One can use `_` to ignore substructure in a pattern. This is used - in the second match expression, so that the shape held in `p` is - left in place. (This does not matter too much since `Shape` - implements `Copy`; it is often more important in cases where the - payload is not copyable, as further discussed below.) +### Both expression- and statement-oriented -Thus, the `White` and `Black` `match`-clauses in the above code bind -`shape` to the associated `PieceShape`, and then casts that shape to -the appropriate integer offset from the king in the unicode codepoint -assignment. (The integers in our original enum definition for -`PieceShape` were selected so that they would match the sequence of -piece ranks in the unicode codepoint assignment.) +Unlike many languages that offer pattern matching, Rust *embraces* +both statement- and expression-oriented programming. -## Mutating enums +Consider writing a function which maps a non-negative integer to a +string rendering it as an ordinal ("1st", "2nd", "3rd", ...). -One event that occurs in chess is that when a pawn reaches the eighth -rank of the board, it is promoted to a another piece of the same color -(usually a queen). +The following code uses range patterns to simplify things, but also, +it is written in a style similar to a `switch` in a statement-oriented +language like C (or C++, Java, et cetera), where the arms of the +`match` are executed for their side-effect alone: -It is easy to write such a promotion in a functional programming -style: - -```rust -fn promote_to_queen_functional(p: Piece) -> Piece { - match p { - Piece::Black(_) => Piece::Black(Queen), - Piece::White(_) => Piece::White(Queen), +```rust,code +fn num_to_ordinal(x: u32) -> String { + let suffix; + match (x % 10, x % 100) { + (1, 1) | (1, 21...91) => { + suffix = "st"; + } + (2, 2) | (2, 22...92) => { + suffix = "nd"; + } + (3, 3) | (3, 23...93) => { + suffix = "rd"; + } + _ => { + suffix = "th"; + } } + return format!("{}{}", x, suffix); } #[test] -fn demo_promote_functional() { - let p = Piece::White(Pawn); - assert_eq!(promote_to_queen_functional(p), Piece::White(Queen)); +fn test_num_to_ordinal() { + assert_eq!(num_to_ordinal( 0), "0th"); + assert_eq!(num_to_ordinal( 1), "1st"); + assert_eq!(num_to_ordinal( 12), "12th"); + assert_eq!(num_to_ordinal( 22), "22nd"); + assert_eq!(num_to_ordinal( 43), "43rd"); + assert_eq!(num_to_ordinal( 67), "67th"); + assert_eq!(num_to_ordinal(1901), "1901st"); } ``` -Let us imagine, however, that we want to model an actual chess board, -and we want to implement promotion as an *in-place* modification; that -is, instead of constructing a whole new copy of the board, we want to -instead use an imperative update to represent the promotion. - -### A first bumbling attempt - -Here is a first, admittedly bumbling, attempt to implement in-place -promotion from a pawn to a queen: It just removes the `-> Piece` -return type and shoehorns an assignment into the code, in the hopes -that everything will work out. - -So, first try: +The Rust compiler accepts the above program; this is notable because +its static analysis is ensuring both that `suffix` is always +initialized before we run the `format!` at the end *and* that `suffix` +is assigned at most once during the function's execution (because if +we could assign `suffix` multiple times, the compiler would force us +to mark `suffix` as mutable). + +To be clear, the above program certainly *can* be written in an +expression-oriented style. The point is that each of the styles has +its use cases, and switching to a statement-oriented style does not +sacrifice every other feature that Rust provides, such as ensuring +that a non-`mut` binding is assigned at most once. + +An important case where this arises is when one wants to +initialize some state and then borrow from it, but only on +*some* control-flow branches. + +```rust,code +fn sometimes_initialize(input: i32) { + let string; + let borrowed; + match input { + 0...100 => { + string = format!("input prints as {}", input); + borrowed = &string[6..]; + } + _ => { + borrowed = "expected between 0 and 100"; + } + } + println!("borrowed: {}", borrowed); -```rust -#[cfg(promote_attempt_1)] -fn promote_to_queen_1(mut pawn: Piece) { - pawn = match pawn { - Piece::Black(_) => Piece::Black(Queen), - Piece::White(_) => Piece::White(Queen), - }; + // (Below would cause compile-time error if uncommented.) + // println!("string: {}", string); } -#[cfg(promote_attempt_1)] #[test] -fn demo_promote_1() { - let p = Piece::White(Pawn); - assert_eq!(p, Piece::White(Pawn)); - promote_to_queen_1(p); - assert_eq!(p, Piece::White(Queen)); +fn demo_sometimes_initialize() { + sometimes_initialize(23); + sometimes_initialize(123); } ``` -Compiling this (enabling it via `--cfg promote_attempt_1`) yields: - -``` -enums.rs:238:5: 238:9 warning: value assigned to `pawn` is never read, #[warn(unused_assignments)] on by default -enums.rs:238 pawn = match pawn { - ^~~~ -``` - -So that's not a good sign. Then running it yields: - -``` -test demo_promote_1 ... FAILED - -failures: - ----- demo_promote_1 stdout ---- -thread 'demo_promote_1' panicked at 'assertion failed: `(left == right)` (left: `White(Pawn)`, right: `White(Queen)`)', enums.rs:250 -``` - -This demonstrates, among other things, that static analysis cannot -stop someone who is determined to write buggy code. But the assertion -failure message is fairly clear about what the problem is: We had -hoped the piece `p` would be replaced with a queen, but it is in fact -still a pawn. - -The crucial mistake being made here is that if you want to mutate a -piece of state in place, then you need to have a mutable *reference* -to that state. The `mut pawn` formal argument to `promote_to_queen` is -indeed mutable, but those mutations are not visible from outside the -function, because the argument has already been moved into the -parameter's location. The invocation `promote_to_queen` is not passing -a reference (mutable or otherwise) to `p`; it is creating a *copy* of -the `Piece`. - -### A second stumble, caught at compile-time - -To make this really concrete, let us consider another kind of data in -our chess board. A chess board is made up of squares, usually -arranged in an 8-by-8 grid. Each `Square` is either unoccupied -(`Empty`) or is occupied by a single piece. - -```rust -#[derive(Clone, Debug, PartialEq, Eq)] -enum Square { - Empty, - Occupied(Piece), +The interesting thing about the above code is that after the `match`, +we are not allowed to directly access `string`, because the compiler +requires that the variable be initialized on every path through the +program. At the same time, we *can* access the data that is held +*within* `string`, because a reference to that data is held by the +`borrowed` variable, which we ensure is initialized on every program +path. (The compiler ensures that no outstanding borrows of the +`string` data could possible outlive `string` itself, and the +generated code ensures that at the end of the scope of `string`, its +data is deallocated if it was previously initialized.) + +In short, for soundness, the Rust language ensures that data is always +initialized before it is referenced, but the designers have attempted +to not require artifices like dummy-initializations inserted solely to +placate such requirements. + +### Algebraic Data Types and Data Invariants + +An `enum` type allows one to define mutually-exclusive classes of +values. The examples shown above used `enum` for simple symbolic tags, +but in Rust, such definitions can define much richer classes of data. + +For example, a binary tree is either a leaf, or an internal node with +references to two child trees. Here is one way to encode a tree of +integers in Rust: + +```rust,code +enum BinaryTree { + Leaf(i32), + Node(Box, i32, Box) } ``` -Note that we have left off the `Copy` derivation; the intention is -that while `Rank` and `Piece` can be freely copied via assignment -statements, the square on a chess board has its own identity, and -should not be freely copied via assignment statements. - -With that in mind, let us try writing `promote_to_queen` again, but -this time passing in a `Square`. - -(This example is also taking advantage of another feature of Rust's -`match` syntax: one can combine multiple match arms that have -identical code into one arm by writing all their patterns pairwise -separated by a vertical bar (`|`)): - - -```rust -#[cfg(promote_attempt_2)] -fn promote_to_queen_2(mut s: Square) { - match s { - Square::Empty => {} - Square::Occupied(Piece::White(mut shape)) | - Square::Occupied(Piece::Black(mut shape)) => { - shape = Queen +(The `Box` type describes an owning reference to a heap-allocated +instance of `V`; if you own a `Box`, then you also own the `V` it +contains, and can mutate it, lend out references to it, et cetera, and +when you finish with the box and let it fall out of scope, it will +automatically clean up the resources associated with the +heap-allocated `V`.) + +The above definition ensures that if we are given a `BinaryTree`, it +will always fall into one of the above two cases. One will never +encounter a `BinaryTree::Node` that does not have a left-hand child. +There is no need to check for null. + +One *does* need to check whether a given `BinaryTree` is a `Leaf` or +az a `Node`, but the compiler statically ensure such checks are done: +you cannot accidentally interpret the data of a `Leaf` as if it were a +`Node`, nor vice versa. + +```rust,code +/// Sum of values in all the nodes and leaves of `t`. +fn tree_weight_v1(t: BinaryTree) -> i32 { + match t { + BinaryTree::Leaf(payload) => payload, + BinaryTree::Node(left, payload, right) => { + tree_weight_v1(*left) + payload + tree_weight_v1(*right) } } } -#[cfg(promote_attempt_2)] +/// Looks like: +/// +/// (4) +/// | +/// +--(2) +/// | | +/// | +--[1] +/// | | +/// | +--[3] +/// | +/// +--[5] +/// +fn sample_tree() -> BinaryTree { + let l1 = Box::new(BinaryTree::Leaf(1)); + let l3 = Box::new(BinaryTree::Leaf(3)); + let n2 = Box::new(BinaryTree::Node(l1, 2, l3)); + let l5 = Box::new(BinaryTree::Leaf(5)); + + BinaryTree::Node(n2, 4, l5) +} + #[test] -fn demo_promote_2() { - let mut s = Square::Occupied(Piece::White(Pawn)); - promote_to_queen_2(s); - assert_eq!(s, Square::Occupied(Piece::White(Queen))); +fn tree_demo_1() { + let tree = sample_tree(); + assert_eq!(tree_weight_v1(tree), (1 + 2 + 3) + 4 + 5); } ``` -This second attempt has the same mistake that we made with -`promote_to_queen_2`: we again have failed to take a mutable reference -to the square, so no in-place update is possible. But *now* we get a -compile-time error: +### Matching L-values -``` -:3:11: 3:23 error: use of moved value: `s` -:3 match ( & ( $ left ) , & ( $ right ) ) { - ^~~~~~~~~~~~ -:1:1: 9:39 note: in expansion of assert_eq! -enums.rs:319:5: 319:58 note: expansion site -enums.rs:318:24: 318:25 note: `s` moved here because it has type `Square`, which is non-copyable -enums.rs:318 promote_to_queen_2(s); - ^ -``` +The previous section described a tree datatype, and showed a program +that computed the sum of the integers in a tree instance. -The error message is telling us exactly what happened: the argument to -`promote_to_queen_2` is moved when the function is called; thus the -attempt to reference `s` in the `assert_eq!` invocation fails, because -`s` has been moved. +That version of `tree_weight` has one big downside, however: it takes +its input tree by value. Once you pass a tree to `tree_weight_v1`, that +tree is gone (as in, deallocated). -(This illustrates why one might *not* add `derive(Copy)` to every data -type where it would otherwise be legal; leaving out `derive(Copy)` can -catch certain bugs.) +```rust,code +#[test] +fn tree_demo_v1_fails() { + let tree = sample_tree(); + assert_eq!(tree_weight_v1(tree), (1 + 2 + 3) + 4 + 5); -### Try, try again; taking a reference + // If you uncomment this line below ... + + // assert_eq!(tree_weight_v1(tree), (1 + 2 + 3) + 4 + 5); -So, here's a third try, where we take a reference to the `Square`. + // ... you will get: error: use of moved value: `tree` +} +``` + +This is *not* a consequence, however, of using `match`; it is rather +a consequence of the function signature that was chosen: ```rust -#[cfg(promote_attempt_3)] -fn promote_to_queen_3(s: &mut Square) { - match s { - &mut Square::Empty => {} - &mut Square::Occupied(Piece::White(mut shape)) | - &mut Square::Occupied(Piece::Black(mut shape)) => { - shape = Queen +fn tree_weight_v1(t: BinaryTree) -> i32 { 0 } +// ^~~~~~~~~~ this means this function takes ownership of `t` +``` + +In fact, in Rust, `match` is designed to work quite well *without* +taking ownership. In particular, the input to `match` is an L-value; +this means that the input expression is evaluated to a *memory +location*, and then match works by inspecting the data at that +location. + +(If the input expression is a variable name or a field/pointer +dereference, then the L-value is just the location of that variable or +field/memory. If the input expression is a function call or other +operation that generates an unnamed temporary value, then it will be +conceptually stored in a temporary area, and that is the memory +location that `match` will inspect.) + +So, if we want a version of `tree_weight` that merely borrows a tree +rather than taking ownership of it, then we will need to make use of +this feature of Rust's `match`. + +```rust,code +/// Sum of values in all the nodes and leaves of `t`. +fn tree_weight_v2(t: &BinaryTree) -> i32 { + // ^~~~~~~~~~~ The `&` means we are *borrowing* the tree + match *t { + BinaryTree::Leaf(payload) => payload, + BinaryTree::Node(ref left, payload, ref right) => { + tree_weight_v2(left) + payload + tree_weight_v2(right) } } } -#[cfg(promote_attempt_3)] #[test] -fn demo_promote_3() { - let mut p = Square::Occupied(Piece::White(Pawn)); - promote_to_queen_3(&mut p); - assert_eq!(p, Square::Occupied(Piece::White(Queen))); +fn tree_demo_2() { + let tree = sample_tree(); + assert_eq!(tree_weight_v2(&tree), (1 + 2 + 3) + 4 + 5); } ``` -We again see the warning: - -``` -enums.rs:355:44: 355:49 warning: variable `shape` is assigned to, but never used, #[warn(unused_variables)] on by default -enums.rs:355 &mut Square::Occupied(Piece::White(mut shape)) | - ^~~~~~~~~ -enums.rs:357:13: 357:14 warning: value assigned to `shape` is never read, #[warn(unused_assignments)] on by default -enums.rs:357 shape = Queen - ^~~~~ -``` - -This warning is again warranted, as the test fails: - -``` -test demo_promote_3 ... FAILED - -failures: - ----- demo_promote_3 stdout ---- -thread 'demo_promote_3' panicked at 'assertion failed: `(left == right)` (left: `Occupied(White(Pawn))`, right: `Occupied(White(Queen))`)', enums.rs:367 -``` - -### Keeping `ref`s into referenced data - -So, what happened here? - -The answer is: Even though we have ensured that `Square` is not -accidentally copied, there is nothing stopping the *contents* of -`Square` from being copied. So in a `match` arm like -``` -&mut Square::Occupied(Piece::Black(mut shape)) => { ... } -``` -if the discriminant value matches that arm, then it will put a -copy of its shape into `shape`. - -This brings us to a very important point about how `match` works in -Rust: it matches its inputs in-place, but normal bindings like the -`mut shape` above will move (or copy) the matched substructure into -fresh locations. - -If you want to capture a reference into the matched substructure, -either to avoid extraneous copying, or to enable modification of the -original input value, then you need to use a `ref`-pattern. - -With this knowledge in hand, let us try again. - -In addition, our fourth try simplifies the code slightly by -dereferencing the argument `*s`. - -```rust -fn promote_to_queen_4(s: &mut Square) { - match *s { - Square::Empty => {} - Square::Occupied(Piece::White(ref mut shape)) | - Square::Occupied(Piece::Black(ref mut shape)) => { - *shape = Queen +The function `tree_weight_v2` looks very much like `tree_weight_v1`. +The only differences are: we take `t` as a borrowed reference (the `&` +in its type), and, importantly, we use `ref`-bindings for `left` and +`right` in the `Node` case. + +The `ref`-binding is a crucial part of how destructuring bind of +L-values works. + +When matching a value of type `T`, an identifier pattern `I` will, on +a successful match, *move* the value out of the original input and +into `I`. Thus we can always conclude in such a case that `I` has type +`T`, or "`I: T`". (For some types `T`, known as *copyable* `T` or "`T` +implements `Copy`", the value will in fact be copied into `I` for such +identifier patterns; but in general types are not copyable; either +way, such bindings do mean that `I` has ownership of a value of type +`T`.) + +Thus, the bindings of `payload` in `tree_weight_v2` both have type +`i32`; the `i32` type implements `Copy`, so the weight is copied into +`payload` in both arms. + +However, when matching a value of type `T`, a `ref`-pattern `ref I` +will, on a successful match, merely *borrow* a reference into the +matched data. In other words, a successful `ref I` match of a value of +type `T` will imply that `I: &T`. Thus, in the `Node` arm of +`tree_weight_v2`, `left` will be reference the left-hand box (which +holds a tree), and `right` will reference the right-hand box (which +also holds a tree). Then we can pass those borrowed references to +trees into the recursive calls to `tree_weight_v2`. + +Likewise, a `ref mut`-pattern (`ref mut I`) will, on a successful +match, take a borrow a *mutable reference* `&mut T`, (which allows +mutation and ensures there are no other active references to that data +at the same time). An important detail here is the destructuring +binding forms like `match` allows one to take mutable references to +disjoint parts of the data simultaneously. + +```rust,code +/// Increment the values in all the nodes and leaves of `t`. +fn tree_grow(t: &mut BinaryTree) { + // ^~~~~~~~~~~~~~~ `&mut`: we have unaliased access to the tree + match *t { + BinaryTree::Leaf(ref mut payload) => *payload += 1, + BinaryTree::Node(ref mut left, ref mut payload, ref mut right) => { + tree_grow(left); + *payload += 1; + tree_grow(right); } } } #[test] -fn demo_promote_4() { - let mut p = Square::Occupied(Piece::White(Pawn)); - promote_to_queen_4(&mut p); - assert_eq!(p, Square::Occupied(Piece::White(Queen))); +fn tree_demo_3() { + let mut tree = sample_tree(); + tree_grow(&mut tree); + assert_eq!(tree_weight_v2(&tree), (2 + 3 + 4) + 5 + 6); } ``` -Compiling and running our code now yields: - -``` -running 7 tests -test demo_debug_format ... ok -test demo_one_letter_collect ... ok -test demo_one_letter ... ok -test demo_debug_format_2 ... ok -test demo_promote_4 ... ok -test check_renders ... ok -test demo_promote_functional ... ok - -test result: ok. 7 passed; 0 failed; 0 ignored; 0 measured -``` - -Huzzah, it works! - -The dereference `*s` works because `match` works on both "L-value" and -"R-value" expressions; L-value expressions evaluate to memory -locations, while R-value expressions evaluate to values that are then -stored, if necessary, in temporary memory locations. It simplified our -code because allowed us to sidestep writing `&mut` at the beginning of -each of our patterns, which was not such a great burden for this tiny -code snippet, but can be much more annoying when working with `match` -expressions that have many more arms, amplifying the redundancy. - -## Avoiding unsoundness +### Avoiding unsoundness (This section is adapted from the Rust demo we presented at the 2014 ML workshop.) @@ -579,7 +527,7 @@ overwrite state secretly, subverting the type-system? Consider the following code: -```rust +```rust,code #[test] fn sound_code() { enum E { A(fn (i8) -> i8), B(usize) } @@ -625,7 +573,7 @@ location and try to interpret it as executable code. But watch: -```rust +```rust,code #[cfg(wont_compile)] #[test] fn unsound_code_1() { @@ -653,8 +601,9 @@ fn unsound_code_1() { } ``` -As you might have inferred from the `cfg` annotation above, this -will not compile. Attempting to compile it yields: +This will not compile. (The `cfg` annotation above actually stops the +compiler from attempting to compile it.) Removing the `cfg` line and +attempting to compile it yields: ``` enums.rs:569:30: 569:51 error: cannot assign to `*p1` because it is borrowed @@ -679,7 +628,7 @@ compiling are not performing a local check of just the single function non-local attempts to subvert the type system by sneaking the overwrite past the static checks are foiled, as shown here: -```rust +```rust,code #[cfg(wont_compile)] #[test] fn unsound_code_2() { @@ -730,11 +679,11 @@ This illustrates why Rust takes such pains to support affine typing of ## Conclusion -Thus ends our tour of enums in Rust. For more information on details -that were not covered here, such as binding via `ident @ pattern`, or -the potentially subtle difference between `{ let id = expr; ... }` -versus `match expr { id => { ... } }`, consult the Rust documentation, -or quiz our awesome community (in `#rust` on IRC, or in the -[user group]). +Thus ends our tour of `match` and enums in Rust. For more information +on details that were not covered here, such as binding via `ident @ +pattern`, or the potentially subtle difference between `{ let id = +expr; ... }` versus `match expr { id => { ... } }`, consult the Rust +documentation, or quiz our awesome community (in `#rust` on IRC, or in +the [user group]). [user group]: http://users.rust-lang.org/ From 13137825f010be16094b4f0c9726b6e057e92483 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Thu, 16 Apr 2015 19:07:24 +0200 Subject: [PATCH 080/147] Try to add some emphasis that pattern matching ergonomics are crucial. --- _posts/2015-04-17-Enums-match-mutation-and-moves.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/_posts/2015-04-17-Enums-match-mutation-and-moves.md b/_posts/2015-04-17-Enums-match-mutation-and-moves.md index 318d36754..5175db1e4 100644 --- a/_posts/2015-04-17-Enums-match-mutation-and-moves.md +++ b/_posts/2015-04-17-Enums-match-mutation-and-moves.md @@ -31,9 +31,9 @@ together are: * `match` embraces both imperative and applicative styles of programming. The compiler's static analyses work hard to ensure - statement-oriented programming remains palatable, leaving the - question of whether expression-orientation is better to style - guides. + statement-oriented programming remains palatable. At the same + time, the `match` construct enables ergonomic case analysis far + beyond what is provided by a C or Java style `switch` statement. * Destructuring bind of *L-values*: Rust encourages the developer to think carefully about ownership and borrowing. To ensure that From 6f46621963985840f33e7ea08dd7437fe02c0062 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Thu, 16 Apr 2015 19:08:21 +0200 Subject: [PATCH 081/147] replace my `rust,code` annotations with `rust`, to get proper highlighting. --- ...15-04-17-Enums-match-mutation-and-moves.md | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/_posts/2015-04-17-Enums-match-mutation-and-moves.md b/_posts/2015-04-17-Enums-match-mutation-and-moves.md index 5175db1e4..2cd527556 100644 --- a/_posts/2015-04-17-Enums-match-mutation-and-moves.md +++ b/_posts/2015-04-17-Enums-match-mutation-and-moves.md @@ -76,7 +76,7 @@ executable; you can cut-and-paste the code snippets into a file `demo.rs`, compile the file with `--test`, and run the resulting binary to see the tests run.) -```rust,code +```rust enum Answer { Higher, Lower, @@ -115,7 +115,7 @@ or that pattern, et cetera. These features are illustrated in the following revision to the guessing-game answer generation strategy: -```rust,code +```rust struct GuessState { guess: u32, answer: Answer, @@ -190,7 +190,7 @@ exposes logical errors. that such expressions always evaluates to a value of the correct type (or jump elsehwere in the program, as illustrated here): -```rust,code +```rust fn suggest_guess_fixed(prior_guess: u32, answer: Answer) { let next_guess = match answer { Answer::Higher => prior_guess + 10, @@ -224,7 +224,7 @@ it is written in a style similar to a `switch` in a statement-oriented language like C (or C++, Java, et cetera), where the arms of the `match` are executed for their side-effect alone: -```rust,code +```rust fn num_to_ordinal(x: u32) -> String { let suffix; match (x % 10, x % 100) { @@ -273,7 +273,7 @@ An important case where this arises is when one wants to initialize some state and then borrow from it, but only on *some* control-flow branches. -```rust,code +```rust fn sometimes_initialize(input: i32) { let string; let borrowed; @@ -325,7 +325,7 @@ For example, a binary tree is either a leaf, or an internal node with references to two child trees. Here is one way to encode a tree of integers in Rust: -```rust,code +```rust enum BinaryTree { Leaf(i32), Node(Box, i32, Box) @@ -349,7 +349,7 @@ az a `Node`, but the compiler statically ensure such checks are done: you cannot accidentally interpret the data of a `Leaf` as if it were a `Node`, nor vice versa. -```rust,code +```rust /// Sum of values in all the nodes and leaves of `t`. fn tree_weight_v1(t: BinaryTree) -> i32 { match t { @@ -397,7 +397,7 @@ That version of `tree_weight` has one big downside, however: it takes its input tree by value. Once you pass a tree to `tree_weight_v1`, that tree is gone (as in, deallocated). -```rust,code +```rust #[test] fn tree_demo_v1_fails() { let tree = sample_tree(); @@ -436,7 +436,7 @@ So, if we want a version of `tree_weight` that merely borrows a tree rather than taking ownership of it, then we will need to make use of this feature of Rust's `match`. -```rust,code +```rust /// Sum of values in all the nodes and leaves of `t`. fn tree_weight_v2(t: &BinaryTree) -> i32 { // ^~~~~~~~~~~ The `&` means we are *borrowing* the tree @@ -492,7 +492,7 @@ at the same time). An important detail here is the destructuring binding forms like `match` allows one to take mutable references to disjoint parts of the data simultaneously. -```rust,code +```rust /// Increment the values in all the nodes and leaves of `t`. fn tree_grow(t: &mut BinaryTree) { // ^~~~~~~~~~~~~~~ `&mut`: we have unaliased access to the tree @@ -527,7 +527,7 @@ overwrite state secretly, subverting the type-system? Consider the following code: -```rust,code +```rust #[test] fn sound_code() { enum E { A(fn (i8) -> i8), B(usize) } @@ -573,7 +573,7 @@ location and try to interpret it as executable code. But watch: -```rust,code +```rust #[cfg(wont_compile)] #[test] fn unsound_code_1() { @@ -628,7 +628,7 @@ compiling are not performing a local check of just the single function non-local attempts to subvert the type system by sneaking the overwrite past the static checks are foiled, as shown here: -```rust,code +```rust #[cfg(wont_compile)] #[test] fn unsound_code_2() { From 9c49d335161808e4d39b0f5f470ee9a82461fe1f Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Thu, 16 Apr 2015 19:15:12 +0200 Subject: [PATCH 082/147] try to improve phrasing of one point about anti-patterns. --- _posts/2015-04-17-Enums-match-mutation-and-moves.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/_posts/2015-04-17-Enums-match-mutation-and-moves.md b/_posts/2015-04-17-Enums-match-mutation-and-moves.md index 2cd527556..4eb6a9c3e 100644 --- a/_posts/2015-04-17-Enums-match-mutation-and-moves.md +++ b/_posts/2015-04-17-Enums-match-mutation-and-moves.md @@ -311,9 +311,11 @@ generated code ensures that at the end of the scope of `string`, its data is deallocated if it was previously initialized.) In short, for soundness, the Rust language ensures that data is always -initialized before it is referenced, but the designers have attempted -to not require artifices like dummy-initializations inserted solely to -placate such requirements. +initialized before it is referenced, but the designers have strived to +avoid requiring artifical coding patterns inserted solely to placate +Rust's static analyses (such as requiring one to initialize `string` +above with some dummy data just so that it can be borrowed later). + ### Algebraic Data Types and Data Invariants From df55592b883c67cb114a32fd7a0cd644a243c78b Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Thu, 16 Apr 2015 19:19:18 +0200 Subject: [PATCH 083/147] Attempt to further emphasize ergonomics in the lead bullets. --- _posts/2015-04-17-Enums-match-mutation-and-moves.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/_posts/2015-04-17-Enums-match-mutation-and-moves.md b/_posts/2015-04-17-Enums-match-mutation-and-moves.md index 4eb6a9c3e..93d9d628e 100644 --- a/_posts/2015-04-17-Enums-match-mutation-and-moves.md +++ b/_posts/2015-04-17-Enums-match-mutation-and-moves.md @@ -26,14 +26,17 @@ In this post we explore how Rust processes such data via `match`. The crucial elements that `match` and its counterpart `enum` tie together are: +* Structural pattern matching of algebraic data types: The `match` + construct enables case analysis with ergonomics that are vastly + improved over that provided by a C or Java style `switch` statement. + * Exhaustive case analysis, which ensures that no case is omitted when processing an input. * `match` embraces both imperative and applicative styles of - programming. The compiler's static analyses work hard to ensure - statement-oriented programming remains palatable. At the same - time, the `match` construct enables ergonomic case analysis far - beyond what is provided by a C or Java style `switch` statement. + programming: The compiler's static analyses work hard to ensure + statement-oriented programming remains palatable, rather than + forcing everyone to adopt an expression-oriented mindset. * Destructuring bind of *L-values*: Rust encourages the developer to think carefully about ownership and borrowing. To ensure that From e8acc0641c08853b3d2dfefec56d00398ad9255b Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Thu, 16 Apr 2015 19:22:28 +0200 Subject: [PATCH 084/147] Try to clarify L-value terminology when first introduced. --- _posts/2015-04-17-Enums-match-mutation-and-moves.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/_posts/2015-04-17-Enums-match-mutation-and-moves.md b/_posts/2015-04-17-Enums-match-mutation-and-moves.md index 93d9d628e..e9271c5fc 100644 --- a/_posts/2015-04-17-Enums-match-mutation-and-moves.md +++ b/_posts/2015-04-17-Enums-match-mutation-and-moves.md @@ -425,10 +425,11 @@ fn tree_weight_v1(t: BinaryTree) -> i32 { 0 } ``` In fact, in Rust, `match` is designed to work quite well *without* -taking ownership. In particular, the input to `match` is an L-value; -this means that the input expression is evaluated to a *memory -location*, and then match works by inspecting the data at that -location. +taking ownership. In particular, the input to `match` is an *L-value +expression*; this means that the input expression is evaluated to a +*memory location* where the value lives (as opposed to an R-value +expression, which conceptually evaluates to the value itself). Then +`match` works by inspecting the data at that location. (If the input expression is a variable name or a field/pointer dereference, then the L-value is just the location of that variable or From 2d208b5b552102d51ea1089a428c58084db5e481 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Thu, 16 Apr 2015 19:26:32 +0200 Subject: [PATCH 085/147] introduce and explain the `tree_grow` example a bit better. --- ...15-04-17-Enums-match-mutation-and-moves.md | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/_posts/2015-04-17-Enums-match-mutation-and-moves.md b/_posts/2015-04-17-Enums-match-mutation-and-moves.md index e9271c5fc..559d0ee80 100644 --- a/_posts/2015-04-17-Enums-match-mutation-and-moves.md +++ b/_posts/2015-04-17-Enums-match-mutation-and-moves.md @@ -498,16 +498,19 @@ at the same time). An important detail here is the destructuring binding forms like `match` allows one to take mutable references to disjoint parts of the data simultaneously. +This code demonstrates this concept by incrementing all of the +values in a given tree. + ```rust /// Increment the values in all the nodes and leaves of `t`. fn tree_grow(t: &mut BinaryTree) { // ^~~~~~~~~~~~~~~ `&mut`: we have unaliased access to the tree - match *t { + match *t { BinaryTree::Leaf(ref mut payload) => *payload += 1, BinaryTree::Node(ref mut left, ref mut payload, ref mut right) => { tree_grow(left); - *payload += 1; - tree_grow(right); + *payload += 1; + tree_grow(right); } } } @@ -515,11 +518,21 @@ fn tree_grow(t: &mut BinaryTree) { #[test] fn tree_demo_3() { let mut tree = sample_tree(); - tree_grow(&mut tree); + tree_grow(&mut tree); assert_eq!(tree_weight_v2(&tree), (2 + 3 + 4) + 5 + 6); } ``` +Note that the code above now binds `payload` by a `ref mut`-pattern; +if it did not use a `ref` pattern, then payload would be bound to a +local copy of the integer, while we want to modify the actual integer +*in the tree itself*. Thus we need a reference to that integer. + +Note also that the code is able to bind `left` and `right` +simultaneously in the `Node` arm. The compiler knows that the two +values cannot alias, and thus it allows both `&mut`-references to live +simultaneously. + ### Avoiding unsoundness (This section is adapted from the Rust demo we presented at the 2014 From b1f261101300a9cfe8ff15e9d2c43b9e218bd81f Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Thu, 16 Apr 2015 19:30:06 +0200 Subject: [PATCH 086/147] Removed the "Avoiding unsoundness" section. --- ...15-04-17-Enums-match-mutation-and-moves.md | 163 ------------------ 1 file changed, 163 deletions(-) diff --git a/_posts/2015-04-17-Enums-match-mutation-and-moves.md b/_posts/2015-04-17-Enums-match-mutation-and-moves.md index 559d0ee80..0374c9c2f 100644 --- a/_posts/2015-04-17-Enums-match-mutation-and-moves.md +++ b/_posts/2015-04-17-Enums-match-mutation-and-moves.md @@ -533,169 +533,6 @@ simultaneously in the `Node` arm. The compiler knows that the two values cannot alias, and thus it allows both `&mut`-references to live simultaneously. -### Avoiding unsoundness - -(This section is adapted from the Rust demo we presented at the 2014 -ML workshop.) - -A potentially non-obvious issue arises when a supposedly-sound -language adds support for `ref mut`, or even just `ref`, in a -non-moving/non-copying `match` expression: How does one prevent -someone from injecting unsound behavior by using mutable references to -overwrite state secretly, subverting the type-system? - -Consider the following code: - -```rust -#[test] -fn sound_code() { - enum E { A(fn (i8) -> i8), B(usize) } - fn add3(x:i8) -> i8 { x + 3 } - let mut a = E::A(add3); let mut b = E::B(19); - { - let m1 = &mut a; let m2 = &mut b; - foo(m1, m2); - } - match b { - E::A(_fn) => println!("b is an E::A"), - E::B(val) => println!("b is an E::B: 0x{:x}", val), - } - - fn foo(p1: &mut E, p2: &mut E) { - match p1 { - &mut E::B(..) => panic!("cannot happen"), - &mut E::A(ref adder) => { - /* WATCH: */ *p2 = E::B(0xBadC0de); - println!("{}", (*adder)(14)); - } - } - } -} -``` - -The above compiles and runs just fine: the references to `a` and `b` -are copied to `p1` and `p2` respectively for the call to `foo`, `b` is -imperatively updated via `p2`, `foo` prints out `17` (= 14 + 3), and -then after `foo` returns it prints out `b is an E::B: 0xbadc0de`. - -The question is: what happens when we tweak the test slightly? - -### Attempted subversion via local overwrites - -For example, what if we imperatively modify `*p1` instead of `*p2`, -but leave the code otherwise unchanged? - -Storing the `E::B(0xBadCode)` would invalidate the data held in -`adder` (indeed, it would put "bad code" there), and so the subsequent -invocation of `adder` will jump to some (potentially invalid) memory -location and try to interpret it as executable code. - -But watch: - -```rust -#[cfg(wont_compile)] -#[test] -fn unsound_code_1() { - enum E { A(fn (i8) -> i8), B(usize) } - fn add3(x:i8) -> i8 { x + 3 } - let mut a = E::A(add3); let mut b = E::B(19); - { - let m1 = &mut a; let m2 = &mut b; - foo(m1, m2); - } - match b { - E::A(_fn) => println!("b is an E::A"), - E::B(val) => println!("b is an E::B: 0x{:x}", val), - } - - fn foo(p1: &mut E, p2: &mut E) { - match p1 { - &mut E::B(..) => panic!("cannot happen"), - &mut E::A(ref adder) => { - /* was p2 */ *p1 = E::B(0xBadC0de); - println!("{}", (*adder)(14)); - } - } - } -} -``` - -This will not compile. (The `cfg` annotation above actually stops the -compiler from attempting to compile it.) Removing the `cfg` line and -attempting to compile it yields: - -``` -enums.rs:569:30: 569:51 error: cannot assign to `*p1` because it is borrowed -enums.rs:569 /* was p2 */ *p1 = E::B(0xBadC0de); - ^~~~~~~~~~~~~~~~~~~~~ -enums.rs:568:23: 568:32 note: borrow of `*p1` occurs here -enums.rs:568 &mut E::A(ref adder) => { - ^~~~~~~~~ -``` - -So, the compiler prevents us from overwriting the function pointer -with bogus data! (Of course, if you *really* want to shoot yourself in -the foot, you can resort to `unsafe` code to sneak the overwrite -through; at that point you are explicitly side-stepping the type -system, which Rust allows.) - -### Attempted subversion via aliasing - -Furthermore, the same rules that prevented the last example from -compiling are not performing a local check of just the single function -`foo`; they are enforcing a *global* soundness property. Other -non-local attempts to subvert the type system by sneaking the -overwrite past the static checks are foiled, as shown here: - -```rust -#[cfg(wont_compile)] -#[test] -fn unsound_code_2() { - enum E { A(fn (i8) -> i8), B(usize) } - fn add3(x:i8) -> i8 { x + 3 } - let mut a = E::A(add3); let mut b = E::B(19); - { - let m1 = &mut a; let m2 = &mut b; - foo(m1, m1); - } - match b { - E::A(_fn) => println!("b is an E::A"), - E::B(val) => println!("b is an E::B: 0x{:x}", val), - } - - fn foo(p1: &mut E, p2: &mut E) { - match p1 { - &mut E::B(..) => panic!("cannot happen"), - &mut E::A(ref adder) => { - /* watch? */ *p2 = E::B(0xBadC0de); - println!("{}", (*adder)(14)); - } - } - } -} -``` - -The above is attempting to create an *alias* between `p1` and `p2`, -thus causing the write to `p2` to actually overwrite the data -we matched in `p1`. - -However, this example also fails to compile: - -``` -enums.rs:612:17: 612:19 error: cannot borrow `*m1` as mutable more than once at a time -enums.rs:612 foo(m1, m1); - ^~ -enums.rs:612:13: 612:15 note: previous borrow of `*m1` occurs here; the mutable borrow prevents subsequent moves, borrows, or modification of `*m1` until the borrow ends -enums.rs:612 foo(m1, m1); - ^~ -enums.rs:612:20: 612:20 note: previous borrow ends here -enums.rs:612 foo(m1, m1); - ^ -``` - -This illustrates why Rust takes such pains to support affine typing of -`&mut T` and other owned types: It is necessary for type soundness! - ## Conclusion Thus ends our tour of `match` and enums in Rust. For more information From 7db2de3c6f2d98dee0ff4e98cfe29fd375785527 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Thu, 16 Apr 2015 19:32:41 +0200 Subject: [PATCH 087/147] fix typo --- _posts/2015-04-17-Enums-match-mutation-and-moves.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_posts/2015-04-17-Enums-match-mutation-and-moves.md b/_posts/2015-04-17-Enums-match-mutation-and-moves.md index 0374c9c2f..6a5fc8366 100644 --- a/_posts/2015-04-17-Enums-match-mutation-and-moves.md +++ b/_posts/2015-04-17-Enums-match-mutation-and-moves.md @@ -191,7 +191,7 @@ exposes logical errors. * Second, since `match` is an expression form, exhaustiveness ensures that such expressions always evaluates to a value of the correct type -(or jump elsehwere in the program, as illustrated here): +(or jump elsewhere in the program, as illustrated here): ```rust fn suggest_guess_fixed(prior_guess: u32, answer: Answer) { From 656df8648079c8b66f72ae43641bac1c1f9be737 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Thu, 16 Apr 2015 19:34:45 +0200 Subject: [PATCH 088/147] fix typo. --- _posts/2015-04-17-Enums-match-mutation-and-moves.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_posts/2015-04-17-Enums-match-mutation-and-moves.md b/_posts/2015-04-17-Enums-match-mutation-and-moves.md index 6a5fc8366..c1b80a4d5 100644 --- a/_posts/2015-04-17-Enums-match-mutation-and-moves.md +++ b/_posts/2015-04-17-Enums-match-mutation-and-moves.md @@ -350,7 +350,7 @@ encounter a `BinaryTree::Node` that does not have a left-hand child. There is no need to check for null. One *does* need to check whether a given `BinaryTree` is a `Leaf` or -az a `Node`, but the compiler statically ensure such checks are done: +as a `Node`, but the compiler statically ensure such checks are done: you cannot accidentally interpret the data of a `Leaf` as if it were a `Node`, nor vice versa. From 1686f8f3754b58e191b7a94d8da2cd5f43e281ec Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Thu, 16 Apr 2015 19:35:13 +0200 Subject: [PATCH 089/147] fix the fix for the previous typo. --- _posts/2015-04-17-Enums-match-mutation-and-moves.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_posts/2015-04-17-Enums-match-mutation-and-moves.md b/_posts/2015-04-17-Enums-match-mutation-and-moves.md index c1b80a4d5..8caea18fe 100644 --- a/_posts/2015-04-17-Enums-match-mutation-and-moves.md +++ b/_posts/2015-04-17-Enums-match-mutation-and-moves.md @@ -350,7 +350,7 @@ encounter a `BinaryTree::Node` that does not have a left-hand child. There is no need to check for null. One *does* need to check whether a given `BinaryTree` is a `Leaf` or -as a `Node`, but the compiler statically ensure such checks are done: +is a `Node`, but the compiler statically ensure such checks are done: you cannot accidentally interpret the data of a `Leaf` as if it were a `Node`, nor vice versa. From 8b74b75d49eb88bd8d8ad248d136ba33b7b12460 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Thu, 16 Apr 2015 19:37:01 +0200 Subject: [PATCH 090/147] formatting improvement: use lowercase `i` for the hypothetical ident patterns. --- ...015-04-17-Enums-match-mutation-and-moves.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/_posts/2015-04-17-Enums-match-mutation-and-moves.md b/_posts/2015-04-17-Enums-match-mutation-and-moves.md index 8caea18fe..9693bdb01 100644 --- a/_posts/2015-04-17-Enums-match-mutation-and-moves.md +++ b/_posts/2015-04-17-Enums-match-mutation-and-moves.md @@ -469,29 +469,29 @@ in its type), and, importantly, we use `ref`-bindings for `left` and The `ref`-binding is a crucial part of how destructuring bind of L-values works. -When matching a value of type `T`, an identifier pattern `I` will, on +When matching a value of type `T`, an identifier pattern `i` will, on a successful match, *move* the value out of the original input and -into `I`. Thus we can always conclude in such a case that `I` has type -`T`, or "`I: T`". (For some types `T`, known as *copyable* `T` or "`T` -implements `Copy`", the value will in fact be copied into `I` for such +into `i`. Thus we can always conclude in such a case that `i` has type +`T`, or "`i: T`". (For some types `T`, known as *copyable* `T` or "`T` +implements `Copy`", the value will in fact be copied into `i` for such identifier patterns; but in general types are not copyable; either -way, such bindings do mean that `I` has ownership of a value of type +way, such bindings do mean that `i` has ownership of a value of type `T`.) Thus, the bindings of `payload` in `tree_weight_v2` both have type `i32`; the `i32` type implements `Copy`, so the weight is copied into `payload` in both arms. -However, when matching a value of type `T`, a `ref`-pattern `ref I` +However, when matching a value of type `T`, a `ref`-pattern `ref i` will, on a successful match, merely *borrow* a reference into the -matched data. In other words, a successful `ref I` match of a value of -type `T` will imply that `I: &T`. Thus, in the `Node` arm of +matched data. In other words, a successful `ref i` match of a value of +type `T` will imply that `i: &T`. Thus, in the `Node` arm of `tree_weight_v2`, `left` will be reference the left-hand box (which holds a tree), and `right` will reference the right-hand box (which also holds a tree). Then we can pass those borrowed references to trees into the recursive calls to `tree_weight_v2`. -Likewise, a `ref mut`-pattern (`ref mut I`) will, on a successful +Likewise, a `ref mut`-pattern (`ref mut i`) will, on a successful match, take a borrow a *mutable reference* `&mut T`, (which allows mutation and ensures there are no other active references to that data at the same time). An important detail here is the destructuring From fe0d990b4e91ff9241a3f873ba6cf4a3328c4aac Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Thu, 16 Apr 2015 19:38:50 +0200 Subject: [PATCH 091/147] fix typo. --- _posts/2015-04-17-Enums-match-mutation-and-moves.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_posts/2015-04-17-Enums-match-mutation-and-moves.md b/_posts/2015-04-17-Enums-match-mutation-and-moves.md index 9693bdb01..bc3c990b1 100644 --- a/_posts/2015-04-17-Enums-match-mutation-and-moves.md +++ b/_posts/2015-04-17-Enums-match-mutation-and-moves.md @@ -492,7 +492,7 @@ also holds a tree). Then we can pass those borrowed references to trees into the recursive calls to `tree_weight_v2`. Likewise, a `ref mut`-pattern (`ref mut i`) will, on a successful -match, take a borrow a *mutable reference* `&mut T`, (which allows +match, borrow a *mutable reference* `&mut T` into the input, (which allows mutation and ensures there are no other active references to that data at the same time). An important detail here is the destructuring binding forms like `match` allows one to take mutable references to From 81f79814ad981f7cc63d31a1cc48c41f04524131 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 17 Apr 2015 01:32:04 +0200 Subject: [PATCH 092/147] incorporated review feedback. --- ...15-04-17-Enums-match-mutation-and-moves.md | 278 +++++++++++------- 1 file changed, 172 insertions(+), 106 deletions(-) diff --git a/_posts/2015-04-17-Enums-match-mutation-and-moves.md b/_posts/2015-04-17-Enums-match-mutation-and-moves.md index bc3c990b1..1085078ca 100644 --- a/_posts/2015-04-17-Enums-match-mutation-and-moves.md +++ b/_posts/2015-04-17-Enums-match-mutation-and-moves.md @@ -33,7 +33,7 @@ together are: * Exhaustive case analysis, which ensures that no case is omitted when processing an input. -* `match` embraces both imperative and applicative styles of +* `match` embraces both imperative and functional styles of programming: The compiler's static analyses work hard to ensure statement-oriented programming remains palatable, rather than forcing everyone to adopt an expression-oriented mindset. @@ -104,7 +104,7 @@ fn demo_suggest_guess() { Patterns can also match structured data (e.g. tuples, slices, user-defined data types) via corresponding patterns. In such patterns, one often -binds substructure of the input to local variables (identifer patterns), +binds substructure of the input to local variables (identifier patterns), for use either in the arm's predicate or in its result. The special `_` pattern matches any single value, and is often used as @@ -132,7 +132,7 @@ fn suggest_guess_smarter(s: GuessState) { println!("we won with {}!", p); } GuessState { answer: Answer::Higher, guess: l, low: _, high: h } | - GuessState { answer: Answer::Lower, guess: h, low: l, high: _ } => { + GuessState { answer: Answer::Lower, guess: h, low: l, high: _ } => { let mid = l + ((h - l) / 2); println!("lets try {} next", mid); } @@ -147,6 +147,10 @@ fn demo_guess_state() { } ``` +This ability to simultaneously perform case analysis *and* bind input +substructure leads to powerful, clear, and concise code, focusing the +reader's attention directly on the data relevant to the case at hand. + That is `match` in a nutshell. So, what is the interplay between this construct and Rust's approach to @@ -154,8 +158,12 @@ ownership and safety in general? ### Exhaustive case analysis -One important method of analytical thinking is case analysis: Dividing -a problem into some number of separate cases, and then analyzing each +`match` enforces exhaustive case analysis, which helps catch bugs in +program logic and ensures that the value of a match expression is +well-defined. + +Case analysis is an important problem solving technique: Dividing a +problem into some number of separate cases, and then analyzing each case individually. For this method of problem solving to work, the cases must be @@ -164,7 +172,9 @@ would mean a potential problem instance for which no solution has been identified. This brings us to one of the fundamental restrictions of Rust's -`match` construct: the collection of provided cases must be exhautive. +`match` construct: the collection of cases must be exhaustive. In +other words, every possible input value must be covered by the pattern +for a least one arm in the match. So, for example, the following code is rejected at compile-time. @@ -199,7 +209,7 @@ fn suggest_guess_fixed(prior_guess: u32, answer: Answer) { Answer::Higher => prior_guess + 10, Answer::Lower => prior_guess - 1, Answer::Bingo => { - println!("we won!"); + println!("we won with {}!", prior_guess); return; } }; @@ -214,11 +224,94 @@ fn demo_guess_fixed() { } ``` +### Algebraic Data Types and Data Invariants + +Algebraic data types succinctly describe classes of data and allow one +to encode rich structural invariants. + +An `enum` type allows one to define mutually-exclusive classes of +values. The examples shown above used `enum` for simple symbolic tags, +but in Rust, such definitions can define much richer classes of data. + +For example, a binary tree is either a leaf, or an internal node with +references to two child trees. Here is one way to encode a tree of +integers in Rust: + +```rust +enum BinaryTree { + Leaf(i32), + Node(Box, i32, Box) +} +``` + +(The `Box` type describes an owning reference to a heap-allocated +instance of `V`; if you own a `Box`, then you also own the `V` it +contains, and can mutate it, lend out references to it, et cetera, and +when you finish with the box and let it fall out of scope, it will +automatically clean up the resources associated with the +heap-allocated `V`.) + +The above definition ensures that if we are given a `BinaryTree`, it +will always fall into one of the above two cases. One will never +encounter a `BinaryTree::Node` that does not have a left-hand child. +There is no need to check for null. + +One *does* need to check whether a given `BinaryTree` is a `Leaf` or +is a `Node`, but the compiler statically ensure such checks are done: +you cannot accidentally interpret the data of a `Leaf` as if it were a +`Node`, nor vice versa. + +```rust +/// Sum of values in all the nodes and leaves of `t`. +fn tree_weight_v1(t: BinaryTree) -> i32 { + match t { + BinaryTree::Leaf(payload) => payload, + BinaryTree::Node(left, payload, right) => { + tree_weight_v1(*left) + payload + tree_weight_v1(*right) + } + } +} + +/// Looks like: +/// +/// (4) +/// | +/// +--(2) +/// | | +/// | +--[1] +/// | | +/// | +--[3] +/// | +/// +--[5] +/// +fn sample_tree() -> BinaryTree { + let l1 = Box::new(BinaryTree::Leaf(1)); + let l3 = Box::new(BinaryTree::Leaf(3)); + let n2 = Box::new(BinaryTree::Node(l1, 2, l3)); + let l5 = Box::new(BinaryTree::Leaf(5)); + + BinaryTree::Node(n2, 4, l5) +} + +#[test] +fn tree_demo_1() { + let tree = sample_tree(); + assert_eq!(tree_weight_v1(tree), (1 + 2 + 3) + 4 + 5); +} +``` + ### Both expression- and statement-oriented Unlike many languages that offer pattern matching, Rust *embraces* both statement- and expression-oriented programming. +Many functional languages that offer pattern matching encourage one to +write in an "expression-oriented style", where the focus is always on +the value returned by combining expressions and evaluating them, and +side-effects are discouraged. This style contrasts with imperative +languages, which encourage a statement-oriented style that focuses on +sequences of commands executed solely for their side-effects. + Consider writing a function which maps a non-negative integer to a string rendering it as an ordinal ("1st", "2nd", "3rd", ...). @@ -267,10 +360,27 @@ we could assign `suffix` multiple times, the compiler would force us to mark `suffix` as mutable). To be clear, the above program certainly *can* be written in an -expression-oriented style. The point is that each of the styles has -its use cases, and switching to a statement-oriented style does not -sacrifice every other feature that Rust provides, such as ensuring -that a non-`mut` binding is assigned at most once. +expression-oriented style in Rust; for example, like so: + +```rust +fn num_to_ordinal_expr(x: u32) -> String { + format!("{}{}", x, match (x % 10, x % 100) { + (1, 1) | (1, 21...91) => "st", + (2, 2) | (2, 22...92) => "nd", + (3, 3) | (3, 23...93) => "rd", + _ => "th" + }) +} +``` + +Sometimes expression-oriented style can yield very succinct code; +other times the style requires contortions that can be more readily +side-stepped when writing in statement-oriented style. + +Each of the styles has its use cases. Crucially, switching to a +statement-oriented style in Rust does not sacrifice every other +feature that Rust provides, such as the guarantee that a non-`mut` +binding is assigned at most once. An important case where this arises is when one wants to initialize some state and then borrow from it, but only on @@ -278,129 +388,69 @@ initialize some state and then borrow from it, but only on ```rust fn sometimes_initialize(input: i32) { - let string; - let borrowed; + let string: String; // a dynamically-constructed string value + let borrowed: &str; // a reference to string data match input { 0...100 => { + // Construct a String on the fly... string = format!("input prints as {}", input); + // ... and then borrow from inside it. borrowed = &string[6..]; } _ => { + // String literals are *already* borrowed references borrowed = "expected between 0 and 100"; } } println!("borrowed: {}", borrowed); - // (Below would cause compile-time error if uncommented.) - // println!("string: {}", string); + // Below would cause compile-time error if uncommented... + + // println!("string: {}", string); + + // ...namely: error: use of possibly uninitialized variable: `string` } #[test] fn demo_sometimes_initialize() { - sometimes_initialize(23); - sometimes_initialize(123); + sometimes_initialize(23); // this invocation will initialize `string` + sometimes_initialize(123); // this one will not } ``` The interesting thing about the above code is that after the `match`, we are not allowed to directly access `string`, because the compiler requires that the variable be initialized on every path through the -program. At the same time, we *can* access the data that is held -*within* `string`, because a reference to that data is held by the -`borrowed` variable, which we ensure is initialized on every program -path. (The compiler ensures that no outstanding borrows of the +program. At the same time, we *can*, via `borrowed`, access the data that +is held *within* `string`, because a reference to that data is held by the +`borrowed` variable when we go through the first match arm, and we +ensure `borrowed` itself is initialized on every execution path +through the program that reaches the `println!` that uses `borrowed`. + +(The compiler ensures that no outstanding borrows of the `string` data could possible outlive `string` itself, and the generated code ensures that at the end of the scope of `string`, its data is deallocated if it was previously initialized.) In short, for soundness, the Rust language ensures that data is always initialized before it is referenced, but the designers have strived to -avoid requiring artifical coding patterns inserted solely to placate +avoid requiring artificial coding patterns inserted solely to placate Rust's static analyses (such as requiring one to initialize `string` above with some dummy data just so that it can be borrowed later). - -### Algebraic Data Types and Data Invariants - -An `enum` type allows one to define mutually-exclusive classes of -values. The examples shown above used `enum` for simple symbolic tags, -but in Rust, such definitions can define much richer classes of data. - -For example, a binary tree is either a leaf, or an internal node with -references to two child trees. Here is one way to encode a tree of -integers in Rust: - -```rust -enum BinaryTree { - Leaf(i32), - Node(Box, i32, Box) -} -``` - -(The `Box` type describes an owning reference to a heap-allocated -instance of `V`; if you own a `Box`, then you also own the `V` it -contains, and can mutate it, lend out references to it, et cetera, and -when you finish with the box and let it fall out of scope, it will -automatically clean up the resources associated with the -heap-allocated `V`.) - -The above definition ensures that if we are given a `BinaryTree`, it -will always fall into one of the above two cases. One will never -encounter a `BinaryTree::Node` that does not have a left-hand child. -There is no need to check for null. - -One *does* need to check whether a given `BinaryTree` is a `Leaf` or -is a `Node`, but the compiler statically ensure such checks are done: -you cannot accidentally interpret the data of a `Leaf` as if it were a -`Node`, nor vice versa. - -```rust -/// Sum of values in all the nodes and leaves of `t`. -fn tree_weight_v1(t: BinaryTree) -> i32 { - match t { - BinaryTree::Leaf(payload) => payload, - BinaryTree::Node(left, payload, right) => { - tree_weight_v1(*left) + payload + tree_weight_v1(*right) - } - } -} - -/// Looks like: -/// -/// (4) -/// | -/// +--(2) -/// | | -/// | +--[1] -/// | | -/// | +--[3] -/// | -/// +--[5] -/// -fn sample_tree() -> BinaryTree { - let l1 = Box::new(BinaryTree::Leaf(1)); - let l3 = Box::new(BinaryTree::Leaf(3)); - let n2 = Box::new(BinaryTree::Node(l1, 2, l3)); - let l5 = Box::new(BinaryTree::Leaf(5)); - - BinaryTree::Node(n2, 4, l5) -} - -#[test] -fn tree_demo_1() { - let tree = sample_tree(); - assert_eq!(tree_weight_v1(tree), (1 + 2 + 3) + 4 + 5); -} -``` - ### Matching L-values -The previous section described a tree datatype, and showed a program -that computed the sum of the integers in a tree instance. +Matching an input can *borrow* input substructure, without taking +ownership; this is crucial for matching a reference (e.g. a value of +type `&T`). + +The "Algebraic Data Types" section above described a tree datatype, and +showed a program that computed the sum of the integers in a tree +instance. That version of `tree_weight` has one big downside, however: it takes its input tree by value. Once you pass a tree to `tree_weight_v1`, that -tree is gone (as in, deallocated). +tree is *gone* (as in, deallocated). ```rust #[test] @@ -427,9 +477,9 @@ fn tree_weight_v1(t: BinaryTree) -> i32 { 0 } In fact, in Rust, `match` is designed to work quite well *without* taking ownership. In particular, the input to `match` is an *L-value expression*; this means that the input expression is evaluated to a -*memory location* where the value lives (as opposed to an R-value -expression, which conceptually evaluates to the value itself). Then -`match` works by inspecting the data at that location. +*memory location* where the value lives. +`match` works by doing this evaluation and then +inspecting the data at that memory location. (If the input expression is a variable name or a field/pointer dereference, then the L-value is just the location of that variable or @@ -463,10 +513,19 @@ fn tree_demo_2() { The function `tree_weight_v2` looks very much like `tree_weight_v1`. The only differences are: we take `t` as a borrowed reference (the `&` -in its type), and, importantly, we use `ref`-bindings for `left` and +in its type), we added a dereference `*t`, and, +importantly, we use `ref`-bindings for `left` and `right` in the `Node` case. -The `ref`-binding is a crucial part of how destructuring bind of +The dereference `*t`, interpreted as an L-value expression, is just +extracting the memory address where the `BinaryTree` is represented +(since the `t: &BinaryTree` is just a *reference* to that data in +memory). The `*t` here is not making a copy of the tree, nor moving it +to a new temporary location, because `match` is treating it as an +L-value. + +The only piece left is the `ref`-binding, which +is a crucial part of how destructuring bind of L-values works. When matching a value of type `T`, an identifier pattern `i` will, on @@ -524,7 +583,7 @@ fn tree_demo_3() { ``` Note that the code above now binds `payload` by a `ref mut`-pattern; -if it did not use a `ref` pattern, then payload would be bound to a +if it did not use a `ref` pattern, then `payload` would be bound to a local copy of the integer, while we want to modify the actual integer *in the tree itself*. Thus we need a reference to that integer. @@ -535,7 +594,14 @@ simultaneously. ## Conclusion -Thus ends our tour of `match` and enums in Rust. For more information +Rust takes the ideas of algebraic data types and pattern matching +pioneered by the functional programming languages, and adapts them to +imperative programming styles and Rust's own ownership and borrowing +systems. The `enum` and `match` forms provide clean data definitions +and expressive power, while static analysis ensures that the resulting +programs are safe. + +For more information on details that were not covered here, such as binding via `ident @ pattern`, or the potentially subtle difference between `{ let id = expr; ... }` versus `match expr { id => { ... } }`, consult the Rust From e1aec240889dac6c09a430ca7b0d2836ef667d63 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 17 Apr 2015 01:46:08 +0200 Subject: [PATCH 093/147] Added links to reference docs on `match` and `L-values`. --- _posts/2015-04-17-Enums-match-mutation-and-moves.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/_posts/2015-04-17-Enums-match-mutation-and-moves.md b/_posts/2015-04-17-Enums-match-mutation-and-moves.md index 1085078ca..5b70b17fd 100644 --- a/_posts/2015-04-17-Enums-match-mutation-and-moves.md +++ b/_posts/2015-04-17-Enums-match-mutation-and-moves.md @@ -475,7 +475,7 @@ fn tree_weight_v1(t: BinaryTree) -> i32 { 0 } ``` In fact, in Rust, `match` is designed to work quite well *without* -taking ownership. In particular, the input to `match` is an *L-value +taking ownership. In particular, the input to `match` is an *[L-value][L_value] expression*; this means that the input expression is evaluated to a *memory location* where the value lives. `match` works by doing this evaluation and then @@ -605,7 +605,9 @@ For more information on details that were not covered here, such as binding via `ident @ pattern`, or the potentially subtle difference between `{ let id = expr; ... }` versus `match expr { id => { ... } }`, consult the Rust -documentation, or quiz our awesome community (in `#rust` on IRC, or in +[documentation][rust_docs], or quiz our awesome community (in `#rust` on IRC, or in the [user group]). +[rust_docs]: https://doc.rust-lang.org/reference.html#match-expressions [user group]: http://users.rust-lang.org/ +[L_value]: https://doc.rust-lang.org/reference.html#lvalues,-rvalues-and-temporaries From 9fbbcad6640c7cb1fdaa7754c582937cd94a338b Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 17 Apr 2015 01:51:29 +0200 Subject: [PATCH 094/147] Improve wording. --- _posts/2015-04-17-Enums-match-mutation-and-moves.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/_posts/2015-04-17-Enums-match-mutation-and-moves.md b/_posts/2015-04-17-Enums-match-mutation-and-moves.md index 5b70b17fd..0977ede10 100644 --- a/_posts/2015-04-17-Enums-match-mutation-and-moves.md +++ b/_posts/2015-04-17-Enums-match-mutation-and-moves.md @@ -374,8 +374,8 @@ fn num_to_ordinal_expr(x: u32) -> String { ``` Sometimes expression-oriented style can yield very succinct code; -other times the style requires contortions that can be more readily -side-stepped when writing in statement-oriented style. +other times the style requires contortions that can be +avoided by writing in a statement-oriented style. Each of the styles has its use cases. Crucially, switching to a statement-oriented style in Rust does not sacrifice every other From b22c1edc5ca68dc18daff6f511cb3e01afdc7bf8 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 17 Apr 2015 11:44:09 +0200 Subject: [PATCH 095/147] Fix issues I noted during a morning review. --- ...015-04-17-Enums-match-mutation-and-moves.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/_posts/2015-04-17-Enums-match-mutation-and-moves.md b/_posts/2015-04-17-Enums-match-mutation-and-moves.md index 0977ede10..4b75838dd 100644 --- a/_posts/2015-04-17-Enums-match-mutation-and-moves.md +++ b/_posts/2015-04-17-Enums-match-mutation-and-moves.md @@ -261,8 +261,10 @@ is a `Node`, but the compiler statically ensure such checks are done: you cannot accidentally interpret the data of a `Leaf` as if it were a `Node`, nor vice versa. +Here is a function that increments all of the integers in a tree +using `match`. + ```rust -/// Sum of values in all the nodes and leaves of `t`. fn tree_weight_v1(t: BinaryTree) -> i32 { match t { BinaryTree::Leaf(payload) => payload, @@ -421,14 +423,14 @@ fn demo_sometimes_initialize() { The interesting thing about the above code is that after the `match`, we are not allowed to directly access `string`, because the compiler requires that the variable be initialized on every path through the -program. At the same time, we *can*, via `borrowed`, access the data that -is held *within* `string`, because a reference to that data is held by the +program. At the same time, we *can*, via `borrowed`, access data that +may held *within* `string`, because a reference to that data is held by the `borrowed` variable when we go through the first match arm, and we ensure `borrowed` itself is initialized on every execution path through the program that reaches the `println!` that uses `borrowed`. (The compiler ensures that no outstanding borrows of the -`string` data could possible outlive `string` itself, and the +`string` data could possibly outlive `string` itself, and the generated code ensures that at the end of the scope of `string`, its data is deallocated if it was previously initialized.) @@ -493,7 +495,6 @@ rather than taking ownership of it, then we will need to make use of this feature of Rust's `match`. ```rust -/// Sum of values in all the nodes and leaves of `t`. fn tree_weight_v2(t: &BinaryTree) -> i32 { // ^~~~~~~~~~~ The `&` means we are *borrowing* the tree match *t { @@ -545,9 +546,9 @@ However, when matching a value of type `T`, a `ref`-pattern `ref i` will, on a successful match, merely *borrow* a reference into the matched data. In other words, a successful `ref i` match of a value of type `T` will imply that `i: &T`. Thus, in the `Node` arm of -`tree_weight_v2`, `left` will be reference the left-hand box (which -holds a tree), and `right` will reference the right-hand box (which -also holds a tree). Then we can pass those borrowed references to +`tree_weight_v2`, `left` will be a reference to the left-hand box (which +holds a tree), and `right` will likewise reference the right-hand tree. +Then we can pass those borrowed references to trees into the recursive calls to `tree_weight_v2`. Likewise, a `ref mut`-pattern (`ref mut i`) will, on a successful @@ -561,7 +562,6 @@ This code demonstrates this concept by incrementing all of the values in a given tree. ```rust -/// Increment the values in all the nodes and leaves of `t`. fn tree_grow(t: &mut BinaryTree) { // ^~~~~~~~~~~~~~~ `&mut`: we have unaliased access to the tree match *t { From c9c3b847c1b6ec735d50846b77daac349a0df8fd Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 17 Apr 2015 11:46:32 +0200 Subject: [PATCH 096/147] move parenthetical note about executing the demo code to after the first demo --- _posts/2015-04-17-Enums-match-mutation-and-moves.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/_posts/2015-04-17-Enums-match-mutation-and-moves.md b/_posts/2015-04-17-Enums-match-mutation-and-moves.md index 4b75838dd..ce56f8b21 100644 --- a/_posts/2015-04-17-Enums-match-mutation-and-moves.md +++ b/_posts/2015-04-17-Enums-match-mutation-and-moves.md @@ -74,11 +74,6 @@ can also match user-defined symbolic data, defined via `enum`. The below code demonstrates generating the next guess (poorly) in a number guessing game, given the answer from a previous guess. -(Incidentally, nearly all the code in this post is directly -executable; you can cut-and-paste the code snippets into a file -`demo.rs`, compile the file with `--test`, and run the resulting -binary to see the tests run.) - ```rust enum Answer { Higher, @@ -102,6 +97,11 @@ fn demo_suggest_guess() { } ``` +(Incidentally, nearly all the code in this post is directly +executable; you can cut-and-paste the code snippets into a file +`demo.rs`, compile the file with `--test`, and run the resulting +binary to see the tests run.) + Patterns can also match structured data (e.g. tuples, slices, user-defined data types) via corresponding patterns. In such patterns, one often binds substructure of the input to local variables (identifier patterns), From 89608a309828576dceb705557cf227163840015b Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 17 Apr 2015 11:48:53 +0200 Subject: [PATCH 097/147] improve suggest_guess_smarter presentation (invariant is low <= guess <= high, so have code reflect that) --- _posts/2015-04-17-Enums-match-mutation-and-moves.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/_posts/2015-04-17-Enums-match-mutation-and-moves.md b/_posts/2015-04-17-Enums-match-mutation-and-moves.md index ce56f8b21..31b0c422e 100644 --- a/_posts/2015-04-17-Enums-match-mutation-and-moves.md +++ b/_posts/2015-04-17-Enums-match-mutation-and-moves.md @@ -131,8 +131,8 @@ fn suggest_guess_smarter(s: GuessState) { GuessState { answer: Answer::Bingo, guess: p, .. } => { println!("we won with {}!", p); } - GuessState { answer: Answer::Higher, guess: l, low: _, high: h } | - GuessState { answer: Answer::Lower, guess: h, low: l, high: _ } => { + GuessState { answer: Answer::Higher, low: _, guess: l, high: h } | + GuessState { answer: Answer::Lower, low: l, guess: h, high: _ } => { let mid = l + ((h - l) / 2); println!("lets try {} next", mid); } From 91d85bb53eafab561eadd8749afa1febb497fea8 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 17 Apr 2015 12:14:55 +0200 Subject: [PATCH 098/147] Hopefully final edit. Inspired by aturon's note to put a summary sentence at the beginning of each subsection (which I did earlier), I tried also adding a very short summary sentence at the end of each section too. In some cases this led me to put in a bit more text just so the text flowed, e.g. going from a final example to the summary sentence. Also, along the way I found a few more ways to clarify the text. Finally, I could not resist adding a quick aside referencing modules and privacy; while I do think that Algebraic Data Types (ADTs) are important for encoding invariants, *Abstract Data Types (ADTs, heh) are if anything even more important for representation invariants, and so I wanted to ensure that I didn't pretend that what is presented here suffices on its own. --- ...15-04-17-Enums-match-mutation-and-moves.md | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/_posts/2015-04-17-Enums-match-mutation-and-moves.md b/_posts/2015-04-17-Enums-match-mutation-and-moves.md index 31b0c422e..4e36134f3 100644 --- a/_posts/2015-04-17-Enums-match-mutation-and-moves.md +++ b/_posts/2015-04-17-Enums-match-mutation-and-moves.md @@ -224,6 +224,14 @@ fn demo_guess_fixed() { } ``` +The `suggest_guess_fixed` function illustrates that `match` can handle +some cases early (and then immediately return from the function), +while computing whatever values are needed from the remaining cases +and letting them fall through to the remainder of the function +body. We can add this special case handling via `match` without fear +of overlooking a case, because `match` will enforce the case +analysis to be exhaustive. + ### Algebraic Data Types and Data Invariants Algebraic data types succinctly describe classes of data and allow one @@ -246,12 +254,12 @@ enum BinaryTree { (The `Box` type describes an owning reference to a heap-allocated instance of `V`; if you own a `Box`, then you also own the `V` it -contains, and can mutate it, lend out references to it, et cetera, and -when you finish with the box and let it fall out of scope, it will +contains, and can mutate it, lend out references to it, et cetera. +When you finish with the box and let it fall out of scope, it will automatically clean up the resources associated with the heap-allocated `V`.) -The above definition ensures that if we are given a `BinaryTree`, it +The above `enum` definition ensures that if we are given a `BinaryTree`, it will always fall into one of the above two cases. One will never encounter a `BinaryTree::Node` that does not have a left-hand child. There is no need to check for null. @@ -302,6 +310,11 @@ fn tree_demo_1() { } ``` +Algebraic data types establish structural invariants that are strictly +enforced by the language. (Even richer representation invariants can +be maintained via the use of modules and privacy; but let us not +digress from the topic at hand.) + ### Both expression- and statement-oriented Unlike many languages that offer pattern matching, Rust *embraces* @@ -378,6 +391,8 @@ fn num_to_ordinal_expr(x: u32) -> String { Sometimes expression-oriented style can yield very succinct code; other times the style requires contortions that can be avoided by writing in a statement-oriented style. +(The ability to return from one `match` arm in the +`suggest_guess_fixed` function earlier was an example of this.) Each of the styles has its use cases. Crucially, switching to a statement-oriented style in Rust does not sacrifice every other @@ -423,7 +438,8 @@ fn demo_sometimes_initialize() { The interesting thing about the above code is that after the `match`, we are not allowed to directly access `string`, because the compiler requires that the variable be initialized on every path through the -program. At the same time, we *can*, via `borrowed`, access data that +program before it can be accessed. +At the same time, we *can*, via `borrowed`, access data that may held *within* `string`, because a reference to that data is held by the `borrowed` variable when we go through the first match arm, and we ensure `borrowed` itself is initialized on every execution path @@ -436,9 +452,9 @@ data is deallocated if it was previously initialized.) In short, for soundness, the Rust language ensures that data is always initialized before it is referenced, but the designers have strived to -avoid requiring artificial coding patterns inserted solely to placate +avoid requiring artificial coding patterns adopted solely to placate Rust's static analyses (such as requiring one to initialize `string` -above with some dummy data just so that it can be borrowed later). +above with some dummy data, or requiring an expression-oriented style). ### Matching L-values From 9757c5ab76cc5d30695250c41dbb010f26346df1 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 17 Apr 2015 12:28:29 +0200 Subject: [PATCH 099/147] couldn't resist adding a sherlock holmes quote. also, decided to point the docs link just to the root page, rather than into the reference manual. (reference.md manual is more complete w.r.t. `match` than e.g. trpl is, but long-term that will hopefully not be the case, and so over time I expect the reference.md to be the wrong thing to have long-lived links to.) --- _posts/2015-04-17-Enums-match-mutation-and-moves.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/_posts/2015-04-17-Enums-match-mutation-and-moves.md b/_posts/2015-04-17-Enums-match-mutation-and-moves.md index 4e36134f3..0c9026a40 100644 --- a/_posts/2015-04-17-Enums-match-mutation-and-moves.md +++ b/_posts/2015-04-17-Enums-match-mutation-and-moves.md @@ -158,6 +158,11 @@ ownership and safety in general? ### Exhaustive case analysis +> ...when you have eliminated all which is impossible, +> then whatever remains, however improbable, must be the truth. +> +> -- Sherlock Holmes (Arthur Conan Doyle, "The Blanched Soldier") + `match` enforces exhaustive case analysis, which helps catch bugs in program logic and ensures that the value of a match expression is well-defined. @@ -624,6 +629,6 @@ expr; ... }` versus `match expr { id => { ... } }`, consult the Rust [documentation][rust_docs], or quiz our awesome community (in `#rust` on IRC, or in the [user group]). -[rust_docs]: https://doc.rust-lang.org/reference.html#match-expressions +[rust_docs]: https://doc.rust-lang.org/ [user group]: http://users.rust-lang.org/ [L_value]: https://doc.rust-lang.org/reference.html#lvalues,-rvalues-and-temporaries From 47ca71a1fe101b1a6eecba99fe3d58fe01f63769 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 17 Apr 2015 12:37:46 +0200 Subject: [PATCH 100/147] improve tree ascii art. --- .../2015-04-17-Enums-match-mutation-and-moves.md | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/_posts/2015-04-17-Enums-match-mutation-and-moves.md b/_posts/2015-04-17-Enums-match-mutation-and-moves.md index 0c9026a40..d88a89889 100644 --- a/_posts/2015-04-17-Enums-match-mutation-and-moves.md +++ b/_posts/2015-04-17-Enums-match-mutation-and-moves.md @@ -287,17 +287,13 @@ fn tree_weight_v1(t: BinaryTree) -> i32 { } } -/// Looks like: +/// Returns tree that Looks like: /// -/// (4) -/// | -/// +--(2) -/// | | -/// | +--[1] -/// | | -/// | +--[3] -/// | -/// +--[5] +/// +----(4)---+ +/// | | +/// +-(2)-+ [5] +/// | | +/// [1] [3] /// fn sample_tree() -> BinaryTree { let l1 = Box::new(BinaryTree::Leaf(1)); From cb9b58dfda2b974b98a9414dc1ab8507e1883eaf Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 17 Apr 2015 12:40:31 +0200 Subject: [PATCH 101/147] small cleanup --- _posts/2015-04-17-Enums-match-mutation-and-moves.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/_posts/2015-04-17-Enums-match-mutation-and-moves.md b/_posts/2015-04-17-Enums-match-mutation-and-moves.md index d88a89889..b15cce265 100644 --- a/_posts/2015-04-17-Enums-match-mutation-and-moves.md +++ b/_posts/2015-04-17-Enums-match-mutation-and-moves.md @@ -323,11 +323,13 @@ both statement- and expression-oriented programming. Many functional languages that offer pattern matching encourage one to write in an "expression-oriented style", where the focus is always on -the value returned by combining expressions and evaluating them, and +the values returned by evaluating combinations of expressions, and side-effects are discouraged. This style contrasts with imperative languages, which encourage a statement-oriented style that focuses on sequences of commands executed solely for their side-effects. +Rust excels in supporting both styles. + Consider writing a function which maps a non-negative integer to a string rendering it as an ordinal ("1st", "2nd", "3rd", ...). From 53d3cc1bd1f52dec22b07334e8eba297b39bdb3c Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 17 Apr 2015 12:42:00 +0200 Subject: [PATCH 102/147] cleanup --- _posts/2015-04-17-Enums-match-mutation-and-moves.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/_posts/2015-04-17-Enums-match-mutation-and-moves.md b/_posts/2015-04-17-Enums-match-mutation-and-moves.md index b15cce265..0851117d3 100644 --- a/_posts/2015-04-17-Enums-match-mutation-and-moves.md +++ b/_posts/2015-04-17-Enums-match-mutation-and-moves.md @@ -370,12 +370,12 @@ fn test_num_to_ordinal() { } ``` -The Rust compiler accepts the above program; this is notable because -its static analysis is ensuring both that `suffix` is always -initialized before we run the `format!` at the end *and* that `suffix` -is assigned at most once during the function's execution (because if -we could assign `suffix` multiple times, the compiler would force us -to mark `suffix` as mutable). +The Rust compiler accepts the above program. This is notable because +its static analyses ensure both: +* `suffix` is always initialized before we run the `format!` at the end +* `suffix` is assigned *at most once* during the function's execution (because if + we could assign `suffix` multiple times, the compiler would force us + to mark `suffix` as mutable). To be clear, the above program certainly *can* be written in an expression-oriented style in Rust; for example, like so: From 09507042b35158994a2a6f58ae4eac73ca2c6e13 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 17 Apr 2015 12:42:41 +0200 Subject: [PATCH 103/147] cleanup --- _posts/2015-04-17-Enums-match-mutation-and-moves.md | 1 + 1 file changed, 1 insertion(+) diff --git a/_posts/2015-04-17-Enums-match-mutation-and-moves.md b/_posts/2015-04-17-Enums-match-mutation-and-moves.md index 0851117d3..e582be225 100644 --- a/_posts/2015-04-17-Enums-match-mutation-and-moves.md +++ b/_posts/2015-04-17-Enums-match-mutation-and-moves.md @@ -373,6 +373,7 @@ fn test_num_to_ordinal() { The Rust compiler accepts the above program. This is notable because its static analyses ensure both: * `suffix` is always initialized before we run the `format!` at the end + of the function, and * `suffix` is assigned *at most once* during the function's execution (because if we could assign `suffix` multiple times, the compiler would force us to mark `suffix` as mutable). From 0f7bf0b33b9390082a2b5f1abc377ed4ea16db14 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 17 Apr 2015 12:46:55 +0200 Subject: [PATCH 104/147] remove double-semicolon that has been irking me. --- _posts/2015-04-17-Enums-match-mutation-and-moves.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_posts/2015-04-17-Enums-match-mutation-and-moves.md b/_posts/2015-04-17-Enums-match-mutation-and-moves.md index e582be225..f40e80d0a 100644 --- a/_posts/2015-04-17-Enums-match-mutation-and-moves.md +++ b/_posts/2015-04-17-Enums-match-mutation-and-moves.md @@ -554,7 +554,7 @@ a successful match, *move* the value out of the original input and into `i`. Thus we can always conclude in such a case that `i` has type `T`, or "`i: T`". (For some types `T`, known as *copyable* `T` or "`T` implements `Copy`", the value will in fact be copied into `i` for such -identifier patterns; but in general types are not copyable; either +identifier patterns. But in general types are not copyable; either way, such bindings do mean that `i` has ownership of a value of type `T`.) From 1d3133d1ee609c4d8e476679a335dbc25a3a80d2 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 17 Apr 2015 12:49:50 +0200 Subject: [PATCH 105/147] Moved discussion of moving/copying into ident patterns into separate paragraph. --- _posts/2015-04-17-Enums-match-mutation-and-moves.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/_posts/2015-04-17-Enums-match-mutation-and-moves.md b/_posts/2015-04-17-Enums-match-mutation-and-moves.md index f40e80d0a..c71d029fb 100644 --- a/_posts/2015-04-17-Enums-match-mutation-and-moves.md +++ b/_posts/2015-04-17-Enums-match-mutation-and-moves.md @@ -552,11 +552,13 @@ L-values works. When matching a value of type `T`, an identifier pattern `i` will, on a successful match, *move* the value out of the original input and into `i`. Thus we can always conclude in such a case that `i` has type -`T`, or "`i: T`". (For some types `T`, known as *copyable* `T` or "`T` -implements `Copy`", the value will in fact be copied into `i` for such -identifier patterns. But in general types are not copyable; either -way, such bindings do mean that `i` has ownership of a value of type -`T`.) +`T`, or "`i: T`". + +For some types `T`, known as *copyable* `T` (also pronounced "`T` +implements `Copy`"), the value will in fact be copied into `i` for such +identifier patterns. In the general case, an arbitrary type `T` is not copyable. +Either way, such identifier pattern bindings do mean that `i` has +ownership of a value of type `T`. Thus, the bindings of `payload` in `tree_weight_v2` both have type `i32`; the `i32` type implements `Copy`, so the weight is copied into From 5c08b9bc4536f910d45cbcf3f7db3e3d0fb3af46 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 17 Apr 2015 12:51:54 +0200 Subject: [PATCH 106/147] cleanup --- _posts/2015-04-17-Enums-match-mutation-and-moves.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_posts/2015-04-17-Enums-match-mutation-and-moves.md b/_posts/2015-04-17-Enums-match-mutation-and-moves.md index c71d029fb..3b5ededf9 100644 --- a/_posts/2015-04-17-Enums-match-mutation-and-moves.md +++ b/_posts/2015-04-17-Enums-match-mutation-and-moves.md @@ -574,7 +574,7 @@ Then we can pass those borrowed references to trees into the recursive calls to `tree_weight_v2`. Likewise, a `ref mut`-pattern (`ref mut i`) will, on a successful -match, borrow a *mutable reference* `&mut T` into the input, (which allows +match, borrow a *mutable reference* into the input, `i: &mut T`, (which allows mutation and ensures there are no other active references to that data at the same time). An important detail here is the destructuring binding forms like `match` allows one to take mutable references to From 35adda5140d7035142dc907fcb57ffc12edbd4a2 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 17 Apr 2015 12:52:48 +0200 Subject: [PATCH 107/147] fix typo. --- _posts/2015-04-17-Enums-match-mutation-and-moves.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/_posts/2015-04-17-Enums-match-mutation-and-moves.md b/_posts/2015-04-17-Enums-match-mutation-and-moves.md index 3b5ededf9..1eed6066a 100644 --- a/_posts/2015-04-17-Enums-match-mutation-and-moves.md +++ b/_posts/2015-04-17-Enums-match-mutation-and-moves.md @@ -576,8 +576,8 @@ trees into the recursive calls to `tree_weight_v2`. Likewise, a `ref mut`-pattern (`ref mut i`) will, on a successful match, borrow a *mutable reference* into the input, `i: &mut T`, (which allows mutation and ensures there are no other active references to that data -at the same time). An important detail here is the destructuring -binding forms like `match` allows one to take mutable references to +at the same time). An important detail here is a destructuring +binding form like `match` allows one to take mutable references to disjoint parts of the data simultaneously. This code demonstrates this concept by incrementing all of the From 4c0261e45603863d6dcf5d9d67d6ec00b079374d Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 17 Apr 2015 12:53:11 +0200 Subject: [PATCH 108/147] cleanup --- _posts/2015-04-17-Enums-match-mutation-and-moves.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_posts/2015-04-17-Enums-match-mutation-and-moves.md b/_posts/2015-04-17-Enums-match-mutation-and-moves.md index 1eed6066a..9e02b8cd3 100644 --- a/_posts/2015-04-17-Enums-match-mutation-and-moves.md +++ b/_posts/2015-04-17-Enums-match-mutation-and-moves.md @@ -576,7 +576,7 @@ trees into the recursive calls to `tree_weight_v2`. Likewise, a `ref mut`-pattern (`ref mut i`) will, on a successful match, borrow a *mutable reference* into the input, `i: &mut T`, (which allows mutation and ensures there are no other active references to that data -at the same time). An important detail here is a destructuring +at the same time). A destructuring binding form like `match` allows one to take mutable references to disjoint parts of the data simultaneously. From 81e18a882bebf17773cad6701e8796848018d25d Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 17 Apr 2015 12:55:17 +0200 Subject: [PATCH 109/147] add hint about `const` to conclusion. --- _posts/2015-04-17-Enums-match-mutation-and-moves.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/_posts/2015-04-17-Enums-match-mutation-and-moves.md b/_posts/2015-04-17-Enums-match-mutation-and-moves.md index 9e02b8cd3..4fca08af5 100644 --- a/_posts/2015-04-17-Enums-match-mutation-and-moves.md +++ b/_posts/2015-04-17-Enums-match-mutation-and-moves.md @@ -624,8 +624,9 @@ and expressive power, while static analysis ensures that the resulting programs are safe. For more information -on details that were not covered here, such as binding via `ident @ -pattern`, or the potentially subtle difference between `{ let id = +on details that were not covered here, such as +defining new named constants, binding via `ident @ pattern`, +or the potentially subtle difference between `{ let id = expr; ... }` versus `match expr { id => { ... } }`, consult the Rust [documentation][rust_docs], or quiz our awesome community (in `#rust` on IRC, or in the [user group]). From 4d2c12a16e8cee00b135fb9c1c65583f0e115e06 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 17 Apr 2015 16:30:56 +0200 Subject: [PATCH 110/147] add comments explaining the patterns in suggest_guess_smarter. --- _posts/2015-04-17-Enums-match-mutation-and-moves.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/_posts/2015-04-17-Enums-match-mutation-and-moves.md b/_posts/2015-04-17-Enums-match-mutation-and-moves.md index 4fca08af5..b3fbfaafd 100644 --- a/_posts/2015-04-17-Enums-match-mutation-and-moves.md +++ b/_posts/2015-04-17-Enums-match-mutation-and-moves.md @@ -128,10 +128,19 @@ struct GuessState { fn suggest_guess_smarter(s: GuessState) { match s { + // First arm only fires on Bingo; it binds `p` to last guess. GuessState { answer: Answer::Bingo, guess: p, .. } => { println!("we won with {}!", p); } + + // Second arm fires if answer was too low or too high. + // We want to find a new guess in the range (l..h), where: + // + // - If it was too low, then we want something higher, so we + // bind the guess to `l` and use our last high guess as `h`. GuessState { answer: Answer::Higher, low: _, guess: l, high: h } | + // - If it was too high, then we want something lower; bind + // the guess to `h` and use our last low guess as `l`. GuessState { answer: Answer::Lower, low: l, guess: h, high: _ } => { let mid = l + ((h - l) / 2); println!("lets try {} next", mid); From e6200ab9a1f6c957d0f7a49edfeb4d77eaea46f6 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 17 Apr 2015 16:51:16 +0200 Subject: [PATCH 111/147] adopt suggestion from niko to use comments to explicitly tease apart the pieces of the patterns in suggest_guess_smarter. --- ...15-04-17-Enums-match-mutation-and-moves.md | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/_posts/2015-04-17-Enums-match-mutation-and-moves.md b/_posts/2015-04-17-Enums-match-mutation-and-moves.md index b3fbfaafd..f870d75e1 100644 --- a/_posts/2015-04-17-Enums-match-mutation-and-moves.md +++ b/_posts/2015-04-17-Enums-match-mutation-and-moves.md @@ -130,6 +130,16 @@ fn suggest_guess_smarter(s: GuessState) { match s { // First arm only fires on Bingo; it binds `p` to last guess. GuessState { answer: Answer::Bingo, guess: p, .. } => { + // ~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~ ~~ + // | | | | + // | | | ignore remaining fields + // | | | + // | | copy value of field `guess` into local variable `p` + // | | + // | Test that `answer field is equal to `Bingo` + // | + // Match against an instance of the struct `GuessState` + println!("we won with {}!", p); } @@ -138,10 +148,28 @@ fn suggest_guess_smarter(s: GuessState) { // // - If it was too low, then we want something higher, so we // bind the guess to `l` and use our last high guess as `h`. - GuessState { answer: Answer::Higher, low: _, guess: l, high: h } | // - If it was too high, then we want something lower; bind // the guess to `h` and use our last low guess as `l`. + GuessState { answer: Answer::Higher, low: _, guess: l, high: h } | GuessState { answer: Answer::Lower, low: l, guess: h, high: _ } => { + // ~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~ ~~~~~~ ~~~~~~~~ ~~~~~~~ + // | | | | | + // | | | | copy or ignore + // | | | | field `high`, + // | | | | as appropriate + // | | | | + // | | | copy field `guess` into + // | | | local variable `l` or `h`, + // | | | as appropriate + // | | | + // | | copy value of field `low` into local + // | | variable `l`, or ignore it, as appropriate + // | | + // | Test that `answer field is equal + // | to `Higher` or `Lower`, as appropriate + // | + // Match against an instance of the struct `GuessState` + let mid = l + ((h - l) / 2); println!("lets try {} next", mid); } From da23b9244fc89e27ac02795f59fc5400fe0d6995 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 17 Apr 2015 17:22:47 +0200 Subject: [PATCH 112/147] Address niko feedback on use of the term "exhaustive." --- ...15-04-17-Enums-match-mutation-and-moves.md | 30 ++++++++----------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/_posts/2015-04-17-Enums-match-mutation-and-moves.md b/_posts/2015-04-17-Enums-match-mutation-and-moves.md index f870d75e1..8a928837b 100644 --- a/_posts/2015-04-17-Enums-match-mutation-and-moves.md +++ b/_posts/2015-04-17-Enums-match-mutation-and-moves.md @@ -200,23 +200,19 @@ ownership and safety in general? > > -- Sherlock Holmes (Arthur Conan Doyle, "The Blanched Soldier") -`match` enforces exhaustive case analysis, which helps catch bugs in -program logic and ensures that the value of a match expression is -well-defined. - -Case analysis is an important problem solving technique: Dividing a -problem into some number of separate cases, and then analyzing each -case individually. - -For this method of problem solving to work, the cases must be -*collectively exhaustive*; otherwise, a case that was not covered -would mean a potential problem instance for which no solution has been -identified. - -This brings us to one of the fundamental restrictions of Rust's -`match` construct: the collection of cases must be exhaustive. In -other words, every possible input value must be covered by the pattern -for a least one arm in the match. +One useful way to tackle a complex problem is to break it down +into individual cases and analyze each case individually. +For this method of problem solving to work, the breakdown must be +*collectively exhaustive*; all of the cases you identified must +actually cover all possible scenarios. + +Using `enum` and `match` in Rust can aid this process, because +`match` enforces exhaustive case analysis: +Every possible input value for a `match` must be covered by the pattern +in a least one arm in the match. + +This helps catch bugs in program logic and ensures that the value of a +`match` expression is well-defined. So, for example, the following code is rejected at compile-time. From 8d4e660889e17e5841cbcb40b23493b1331f6e1a Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 17 Apr 2015 17:25:26 +0200 Subject: [PATCH 113/147] add bullet about exhaustiveness checking being a refactoring aid, as suggested by niko. --- .../2015-04-17-Enums-match-mutation-and-moves.md | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/_posts/2015-04-17-Enums-match-mutation-and-moves.md b/_posts/2015-04-17-Enums-match-mutation-and-moves.md index 8a928837b..8714499d4 100644 --- a/_posts/2015-04-17-Enums-match-mutation-and-moves.md +++ b/_posts/2015-04-17-Enums-match-mutation-and-moves.md @@ -237,9 +237,18 @@ Rust has this restriction for two reasons: general solution if the cases are exhaustive. Exhaustiveness-checking exposes logical errors. -* Second, since `match` is an expression form, exhaustiveness ensures -that such expressions always evaluates to a value of the correct type -(or jump elsewhere in the program, as illustrated here): +* Second, exhaustiveness-checking can act as a refactoring aid. During +the development process, I often add new variants for a particular +`enum` definition. The exhaustiveness-check helps points out all of +the `match` expressions were I only wrote the cases from the prior +version of the `enum` type. + +* Third, since `match` is an expression form, exhaustiveness ensures +that such expressions always evaluates to a value of the correct type, +or jump elsewhere in the program. + +The following code is a fixed version of the `suggest_guess_broken` +function we saw above; it directly illustrates "jumping elsewhere": ```rust fn suggest_guess_fixed(prior_guess: u32, answer: Answer) { From f0a72062ad879535016bfad0defc98e3483a3921 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 17 Apr 2015 17:28:20 +0200 Subject: [PATCH 114/147] I meant to commit this earlier. --- _posts/2015-04-17-Enums-match-mutation-and-moves.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/_posts/2015-04-17-Enums-match-mutation-and-moves.md b/_posts/2015-04-17-Enums-match-mutation-and-moves.md index 8714499d4..72e9e69ea 100644 --- a/_posts/2015-04-17-Enums-match-mutation-and-moves.md +++ b/_posts/2015-04-17-Enums-match-mutation-and-moves.md @@ -275,8 +275,10 @@ The `suggest_guess_fixed` function illustrates that `match` can handle some cases early (and then immediately return from the function), while computing whatever values are needed from the remaining cases and letting them fall through to the remainder of the function -body. We can add this special case handling via `match` without fear -of overlooking a case, because `match` will enforce the case +body. + +We can add such special case handling via `match` without fear +of overlooking a case, because `match` will force the case analysis to be exhaustive. ### Algebraic Data Types and Data Invariants From b24a3b1111612d4241a76b5e20ec6d9a3f9a1822 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 17 Apr 2015 17:28:42 +0200 Subject: [PATCH 115/147] Make capitalization and alignment consistent, as suggested by niko. --- .../2015-04-17-Enums-match-mutation-and-moves.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/_posts/2015-04-17-Enums-match-mutation-and-moves.md b/_posts/2015-04-17-Enums-match-mutation-and-moves.md index 72e9e69ea..b05a3fe57 100644 --- a/_posts/2015-04-17-Enums-match-mutation-and-moves.md +++ b/_posts/2015-04-17-Enums-match-mutation-and-moves.md @@ -132,9 +132,9 @@ fn suggest_guess_smarter(s: GuessState) { GuessState { answer: Answer::Bingo, guess: p, .. } => { // ~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~ ~~ // | | | | - // | | | ignore remaining fields + // | | | Ignore remaining fields // | | | - // | | copy value of field `guess` into local variable `p` + // | | Copy value of field `guess` into local variable `p` // | | // | Test that `answer field is equal to `Bingo` // | @@ -154,19 +154,19 @@ fn suggest_guess_smarter(s: GuessState) { GuessState { answer: Answer::Lower, low: l, guess: h, high: _ } => { // ~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~ ~~~~~~ ~~~~~~~~ ~~~~~~~ // | | | | | - // | | | | copy or ignore + // | | | | Copy or ignore // | | | | field `high`, // | | | | as appropriate // | | | | - // | | | copy field `guess` into + // | | | Copy field `guess` into // | | | local variable `l` or `h`, - // | | | as appropriate + // | | | as appropriate // | | | - // | | copy value of field `low` into local + // | | Copy value of field `low` into local // | | variable `l`, or ignore it, as appropriate // | | // | Test that `answer field is equal - // | to `Higher` or `Lower`, as appropriate + // | to `Higher` or `Lower`, as appropriate // | // Match against an instance of the struct `GuessState` From 5301ce1338732f099f5b13c959c9a7856c11a7ed Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 17 Apr 2015 17:38:26 +0200 Subject: [PATCH 116/147] remove much of the jargon from the bullets in the intro. --- ...15-04-17-Enums-match-mutation-and-moves.md | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/_posts/2015-04-17-Enums-match-mutation-and-moves.md b/_posts/2015-04-17-Enums-match-mutation-and-moves.md index b05a3fe57..88413a49c 100644 --- a/_posts/2015-04-17-Enums-match-mutation-and-moves.md +++ b/_posts/2015-04-17-Enums-match-mutation-and-moves.md @@ -26,24 +26,22 @@ In this post we explore how Rust processes such data via `match`. The crucial elements that `match` and its counterpart `enum` tie together are: -* Structural pattern matching of algebraic data types: The `match` - construct enables case analysis with ergonomics that are vastly - improved over that provided by a C or Java style `switch` statement. +* Structural pattern matching: case analysis with ergonomics vastly + improved over a C or Java style `switch` statement. -* Exhaustive case analysis, which ensures that no case is omitted +* Exhaustive case analysis: ensures that no case is omitted when processing an input. * `match` embraces both imperative and functional styles of - programming: The compiler's static analyses work hard to ensure - statement-oriented programming remains palatable, rather than - forcing everyone to adopt an expression-oriented mindset. + programming: you can continue using `break` statements, assignments, + et cetera, + rather than being forced to adopt an expression-oriented mindset. -* Destructuring bind of *L-values*: Rust encourages the developer to +* `match` "borrows" or "moves", as needed: Rust encourages the developer to think carefully about ownership and borrowing. To ensure that - processing data does not force one to give up ownership of a value + one is not forced to yield ownership of a value prematurely, `match` is designed with support for merely *borrowing* - substructure within its input (as opposed to always *moving* such - substructure). + substructure (as opposed to always *moving* such substructure). We cover each of the items above in detail below, but first we establish a foundation for the discussion: What does `match` look From f26018eaea31e9a6cacb4ed27502223220657f12 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 17 Apr 2015 17:40:22 +0200 Subject: [PATCH 117/147] switch from "PREDICATE" to "PATTERNS" justification: this is not a formal model of the language. Even though *I* think of the whole thing as a predicate, it is just as likely to confuse people, especially since I do not even introduce side-conditions at this point. --- _posts/2015-04-17-Enums-match-mutation-and-moves.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/_posts/2015-04-17-Enums-match-mutation-and-moves.md b/_posts/2015-04-17-Enums-match-mutation-and-moves.md index 88413a49c..4644d4ea9 100644 --- a/_posts/2015-04-17-Enums-match-mutation-and-moves.md +++ b/_posts/2015-04-17-Enums-match-mutation-and-moves.md @@ -53,17 +53,17 @@ The `match` expression in Rust has this form: ```rust match INPUT_EXPRESSION { - PREDICATE_1 => RESULT_EXPRESSION_1, - PREDICATE_2 => RESULT_EXPRESSION_2, + PATTERNS_1 => RESULT_EXPRESSION_1, + PATTERNS_2 => RESULT_EXPRESSION_2, ... - PREDICATE_n => RESULT_EXPRESSION_n + PATTERNS_n => RESULT_EXPRESSION_n } ``` -where each of the `PREDICATE_i` contains at least one *pattern*. A +where each of the `PATTERNS_i` contains at least one *pattern*. A pattern describes a subset of the possible values to which `INPUT_EXPRESSION` could evaluate. -The syntax `PREDICATE => RESULT_EXPRESSION` is called a "match arm", +The syntax `PATTERNS => RESULT_EXPRESSION` is called a "match arm", or simply "arm". Patterns can match atomic values, like integers or characters; they From 02593b22ffcf20b92ca4e98afd11f0cfdfb53949 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 17 Apr 2015 17:51:36 +0200 Subject: [PATCH 118/147] Tried to make the post less jargon-heavy. Many thanks to Mutabah, proc, libfud, asQuirrel, annodomini and the rest of `#rust` for the review feedback! --- _posts/2015-04-17-Enums-match-mutation-and-moves.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/_posts/2015-04-17-Enums-match-mutation-and-moves.md b/_posts/2015-04-17-Enums-match-mutation-and-moves.md index 4644d4ea9..6e0a9c29d 100644 --- a/_posts/2015-04-17-Enums-match-mutation-and-moves.md +++ b/_posts/2015-04-17-Enums-match-mutation-and-moves.md @@ -8,7 +8,7 @@ description: "A tour of matching and enums in Rust." One of the primary goals of the Rust project is to enable safe systems programming. Systems programming usually implies imperative programming, which in turns often implies side-effects, reasoning -about shared, aliasable state, et cetera. +about shared state, et cetera. At the same time, to provide *safety*, Rust programs and data types must be structured in a way that allows static checking to ensure @@ -66,7 +66,7 @@ pattern describes a subset of the possible values to which The syntax `PATTERNS => RESULT_EXPRESSION` is called a "match arm", or simply "arm". -Patterns can match atomic values, like integers or characters; they +Patterns can match simple values like integers or characters; they can also match user-defined symbolic data, defined via `enum`. The below code demonstrates generating the next guess (poorly) in a number @@ -100,10 +100,10 @@ executable; you can cut-and-paste the code snippets into a file `demo.rs`, compile the file with `--test`, and run the resulting binary to see the tests run.) -Patterns can also match structured data (e.g. tuples, slices, user-defined +Patterns can also match [structured data] (e.g. tuples, slices, user-defined data types) via corresponding patterns. In such patterns, one often -binds substructure of the input to local variables (identifier patterns), -for use either in the arm's predicate or in its result. +binds parts of the input to local variables; +those variables can then be used in the result expression. The special `_` pattern matches any single value, and is often used as a catch-all; the special `..` pattern generalizes this by matching any @@ -627,7 +627,7 @@ values in a given tree. ```rust fn tree_grow(t: &mut BinaryTree) { - // ^~~~~~~~~~~~~~~ `&mut`: we have unaliased access to the tree + // ^~~~~~~~~~~~~~~ `&mut`: we have exclusive access to the tree match *t { BinaryTree::Leaf(ref mut payload) => *payload += 1, BinaryTree::Node(ref mut left, ref mut payload, ref mut right) => { @@ -673,6 +673,7 @@ expr; ... }` versus `match expr { id => { ... } }`, consult the Rust [documentation][rust_docs], or quiz our awesome community (in `#rust` on IRC, or in the [user group]). +[structured data]: http://en.wikipedia.org/wiki/Record_%28computer_science%29 [rust_docs]: https://doc.rust-lang.org/ [user group]: http://users.rust-lang.org/ [L_value]: https://doc.rust-lang.org/reference.html#lvalues,-rvalues-and-temporaries From e25a759dca1a64e70ea09a9350da39619f8279e3 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 17 Apr 2015 17:57:53 +0200 Subject: [PATCH 119/147] Added acknowledgements of review helpers. :) --- _posts/2015-04-17-Enums-match-mutation-and-moves.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/_posts/2015-04-17-Enums-match-mutation-and-moves.md b/_posts/2015-04-17-Enums-match-mutation-and-moves.md index 6e0a9c29d..940ea2e85 100644 --- a/_posts/2015-04-17-Enums-match-mutation-and-moves.md +++ b/_posts/2015-04-17-Enums-match-mutation-and-moves.md @@ -673,6 +673,10 @@ expr; ... }` versus `match expr { id => { ... } }`, consult the Rust [documentation][rust_docs], or quiz our awesome community (in `#rust` on IRC, or in the [user group]). +(Many thanks to those who helped review this post, especially Aaron Turon +and Niko Matsakis, as well as +`Mutabah`, `proc`, `libfud`, `asQuirrel`, and `annodomini` from `#rust`.) + [structured data]: http://en.wikipedia.org/wiki/Record_%28computer_science%29 [rust_docs]: https://doc.rust-lang.org/ [user group]: http://users.rust-lang.org/ From bfdd20002981909ead18ecb0dd68a086c3b36e36 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 17 Apr 2015 18:02:47 +0200 Subject: [PATCH 120/147] avoid naming number of elements in the bullet list. ;) --- _posts/2015-04-17-Enums-match-mutation-and-moves.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_posts/2015-04-17-Enums-match-mutation-and-moves.md b/_posts/2015-04-17-Enums-match-mutation-and-moves.md index 940ea2e85..7da70afbe 100644 --- a/_posts/2015-04-17-Enums-match-mutation-and-moves.md +++ b/_posts/2015-04-17-Enums-match-mutation-and-moves.md @@ -229,7 +229,7 @@ Many other languages offer a pattern matching construct (ML and various macro-based `match` implementations in Scheme both come to mind), but not all of them have this restriction. -Rust has this restriction for two reasons: +Rust has this restriction for these reasons: * First, as noted above, dividing a problem into cases only yields a general solution if the cases are exhaustive. Exhaustiveness-checking From 466ce858ad35f0efeedd094ce3f16251fa517d15 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 17 Apr 2015 18:03:17 +0200 Subject: [PATCH 121/147] fix typo. --- _posts/2015-04-17-Enums-match-mutation-and-moves.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_posts/2015-04-17-Enums-match-mutation-and-moves.md b/_posts/2015-04-17-Enums-match-mutation-and-moves.md index 7da70afbe..6ab1abf64 100644 --- a/_posts/2015-04-17-Enums-match-mutation-and-moves.md +++ b/_posts/2015-04-17-Enums-match-mutation-and-moves.md @@ -238,7 +238,7 @@ exposes logical errors. * Second, exhaustiveness-checking can act as a refactoring aid. During the development process, I often add new variants for a particular `enum` definition. The exhaustiveness-check helps points out all of -the `match` expressions were I only wrote the cases from the prior +the `match` expressions where I only wrote the cases from the prior version of the `enum` type. * Third, since `match` is an expression form, exhaustiveness ensures From 4dbf9df543722569cf30ade3b57c433a83aae9be Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 17 Apr 2015 18:03:58 +0200 Subject: [PATCH 122/147] fix typo. --- _posts/2015-04-17-Enums-match-mutation-and-moves.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/_posts/2015-04-17-Enums-match-mutation-and-moves.md b/_posts/2015-04-17-Enums-match-mutation-and-moves.md index 6ab1abf64..6d5b91d4e 100644 --- a/_posts/2015-04-17-Enums-match-mutation-and-moves.md +++ b/_posts/2015-04-17-Enums-match-mutation-and-moves.md @@ -242,8 +242,8 @@ the `match` expressions where I only wrote the cases from the prior version of the `enum` type. * Third, since `match` is an expression form, exhaustiveness ensures -that such expressions always evaluates to a value of the correct type, -or jump elsewhere in the program. +that such expressions always either evaluate to a value of the correct type, +*or* jump elsewhere in the program. The following code is a fixed version of the `suggest_guess_broken` function we saw above; it directly illustrates "jumping elsewhere": From fc2d18dc0605aabb86f11a50b4261d668ac7081f Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 17 Apr 2015 18:06:03 +0200 Subject: [PATCH 123/147] add reference for Algebraic Data Types so people can look up the term. --- _posts/2015-04-17-Enums-match-mutation-and-moves.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/_posts/2015-04-17-Enums-match-mutation-and-moves.md b/_posts/2015-04-17-Enums-match-mutation-and-moves.md index 6d5b91d4e..18559caf3 100644 --- a/_posts/2015-04-17-Enums-match-mutation-and-moves.md +++ b/_posts/2015-04-17-Enums-match-mutation-and-moves.md @@ -279,9 +279,9 @@ We can add such special case handling via `match` without fear of overlooking a case, because `match` will force the case analysis to be exhaustive. -### Algebraic Data Types and Data Invariants +### Algebraic Data Types and Structural Invariants -Algebraic data types succinctly describe classes of data and allow one +[Algebraic data types] succinctly describe classes of data and allow one to encode rich structural invariants. An `enum` type allows one to define mutually-exclusive classes of @@ -678,6 +678,7 @@ and Niko Matsakis, as well as `Mutabah`, `proc`, `libfud`, `asQuirrel`, and `annodomini` from `#rust`.) [structured data]: http://en.wikipedia.org/wiki/Record_%28computer_science%29 +[Algebraic data types]: http://en.wikipedia.org/wiki/Algebraic_data_type [rust_docs]: https://doc.rust-lang.org/ [user group]: http://users.rust-lang.org/ [L_value]: https://doc.rust-lang.org/reference.html#lvalues,-rvalues-and-temporaries From 70b7ceb0f5ce4a8edea037c364bda60064fe1ec7 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 17 Apr 2015 18:07:49 +0200 Subject: [PATCH 124/147] added link to above section. --- _posts/2015-04-17-Enums-match-mutation-and-moves.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/_posts/2015-04-17-Enums-match-mutation-and-moves.md b/_posts/2015-04-17-Enums-match-mutation-and-moves.md index 18559caf3..85c14e827 100644 --- a/_posts/2015-04-17-Enums-match-mutation-and-moves.md +++ b/_posts/2015-04-17-Enums-match-mutation-and-moves.md @@ -280,6 +280,7 @@ of overlooking a case, because `match` will force the case analysis to be exhaustive. ### Algebraic Data Types and Structural Invariants +[adts]: #algebraic-data-types-and-structural-invariants [Algebraic data types] succinctly describe classes of data and allow one to encode rich structural invariants. @@ -508,7 +509,7 @@ Matching an input can *borrow* input substructure, without taking ownership; this is crucial for matching a reference (e.g. a value of type `&T`). -The "Algebraic Data Types" section above described a tree datatype, and +The ["Algebraic Data Types" section][adts] above described a tree datatype, and showed a program that computed the sum of the integers in a tree instance. From ab09f522f18eeaff1ff78bc370e5ee8d0ec2dbad Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 17 Apr 2015 18:08:24 +0200 Subject: [PATCH 125/147] fix typo. --- _posts/2015-04-17-Enums-match-mutation-and-moves.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_posts/2015-04-17-Enums-match-mutation-and-moves.md b/_posts/2015-04-17-Enums-match-mutation-and-moves.md index 85c14e827..b580f4fbb 100644 --- a/_posts/2015-04-17-Enums-match-mutation-and-moves.md +++ b/_posts/2015-04-17-Enums-match-mutation-and-moves.md @@ -313,7 +313,7 @@ encounter a `BinaryTree::Node` that does not have a left-hand child. There is no need to check for null. One *does* need to check whether a given `BinaryTree` is a `Leaf` or -is a `Node`, but the compiler statically ensure such checks are done: +is a `Node`, but the compiler statically ensures such checks are done: you cannot accidentally interpret the data of a `Leaf` as if it were a `Node`, nor vice versa. From 04f9d064b376bcbb7551afd5fc16eb7aca44624d Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 17 Apr 2015 18:11:00 +0200 Subject: [PATCH 126/147] add link to the `fn suggest_guess_fixed` example. --- _posts/2015-04-17-Enums-match-mutation-and-moves.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/_posts/2015-04-17-Enums-match-mutation-and-moves.md b/_posts/2015-04-17-Enums-match-mutation-and-moves.md index b580f4fbb..583bb3ad2 100644 --- a/_posts/2015-04-17-Enums-match-mutation-and-moves.md +++ b/_posts/2015-04-17-Enums-match-mutation-and-moves.md @@ -245,6 +245,9 @@ version of the `enum` type. that such expressions always either evaluate to a value of the correct type, *or* jump elsewhere in the program. +#### Jumping out of a match +[jumping]: #jumping-out-of-a-match + The following code is a fixed version of the `suggest_guess_broken` function we saw above; it directly illustrates "jumping elsewhere": @@ -439,7 +442,7 @@ Sometimes expression-oriented style can yield very succinct code; other times the style requires contortions that can be avoided by writing in a statement-oriented style. (The ability to return from one `match` arm in the -`suggest_guess_fixed` function earlier was an example of this.) +`suggest_guess_fixed` function [earlier][jumping] was an example of this.) Each of the styles has its use cases. Crucially, switching to a statement-oriented style in Rust does not sacrifice every other From 6ee1e32f929468b6ce32773a8445ce3e9f389fe8 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 17 Apr 2015 18:13:26 +0200 Subject: [PATCH 127/147] avoid using the term "L-value" prematurely. --- _posts/2015-04-17-Enums-match-mutation-and-moves.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/_posts/2015-04-17-Enums-match-mutation-and-moves.md b/_posts/2015-04-17-Enums-match-mutation-and-moves.md index 583bb3ad2..11a2dbf7c 100644 --- a/_posts/2015-04-17-Enums-match-mutation-and-moves.md +++ b/_posts/2015-04-17-Enums-match-mutation-and-moves.md @@ -506,7 +506,8 @@ avoid requiring artificial coding patterns adopted solely to placate Rust's static analyses (such as requiring one to initialize `string` above with some dummy data, or requiring an expression-oriented style). -### Matching L-values +### Matching without moving +[matching without moving]: #matching-without-moving Matching an input can *borrow* input substructure, without taking ownership; this is crucial for matching a reference (e.g. a value of From a4047a9c487367fd22a6403aa4ff44a23f369a7a Mon Sep 17 00:00:00 2001 From: Felix S Klock II Date: Fri, 17 Apr 2015 18:25:59 +0200 Subject: [PATCH 128/147] break up dense text in Matching without Moving improve Matching without Moving section by breaking up dense text --- ...15-04-17-Enums-match-mutation-and-moves.md | 39 +++++++++++-------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/_posts/2015-04-17-Enums-match-mutation-and-moves.md b/_posts/2015-04-17-Enums-match-mutation-and-moves.md index 11a2dbf7c..14686d671 100644 --- a/_posts/2015-04-17-Enums-match-mutation-and-moves.md +++ b/_posts/2015-04-17-Enums-match-mutation-and-moves.md @@ -596,34 +596,39 @@ The only piece left is the `ref`-binding, which is a crucial part of how destructuring bind of L-values works. -When matching a value of type `T`, an identifier pattern `i` will, on -a successful match, *move* the value out of the original input and -into `i`. Thus we can always conclude in such a case that `i` has type -`T`, or "`i: T`". +* When matching a value of type `T`, an identifier pattern `i` will, on + a successful match, *move* the value out of the original input and + into `i`. Thus we can always conclude in such a case that `i` has type + `T` (or more succinctly, "`i: T`"). -For some types `T`, known as *copyable* `T` (also pronounced "`T` -implements `Copy`"), the value will in fact be copied into `i` for such -identifier patterns. In the general case, an arbitrary type `T` is not copyable. -Either way, such identifier pattern bindings do mean that `i` has -ownership of a value of type `T`. + For some types `T`, known as *copyable* `T` (also pronounced "`T` + implements `Copy`"), the value will in fact be copied into `i` for such + identifier patterns. (Note that in general, an arbitrary type `T` is not copyable.) + + Either way, such pattern bindings do mean that the variable `i` has + *ownership* of a value of type `T`. Thus, the bindings of `payload` in `tree_weight_v2` both have type `i32`; the `i32` type implements `Copy`, so the weight is copied into `payload` in both arms. -However, when matching a value of type `T`, a `ref`-pattern `ref i` -will, on a successful match, merely *borrow* a reference into the -matched data. In other words, a successful `ref i` match of a value of -type `T` will imply that `i: &T`. Thus, in the `Node` arm of +* However, when matching a value of type `T`, a `ref`-pattern `ref i` + will, on a successful match, merely *borrow* a reference into the + matched data. In other words, a successful `ref i` match of a value of + type `T` will imply that `i` has the type of a *reference* to `T` + (or more succinctly, "`i: &T`"). + +Thus, in the `Node` arm of `tree_weight_v2`, `left` will be a reference to the left-hand box (which holds a tree), and `right` will likewise reference the right-hand tree. -Then we can pass those borrowed references to -trees into the recursive calls to `tree_weight_v2`. + +We can pass these borrowed references to trees into the recursive calls to `tree_weight_v2`, +as the code demonstrates. Likewise, a `ref mut`-pattern (`ref mut i`) will, on a successful -match, borrow a *mutable reference* into the input, `i: &mut T`, (which allows +match, borrow a *mutable reference* into the input: `i: &mut T`. This allows mutation and ensures there are no other active references to that data -at the same time). A destructuring +at the same time. A destructuring binding form like `match` allows one to take mutable references to disjoint parts of the data simultaneously. From e528d4c6c136ef9b3bac61172d3f0946ece260ff Mon Sep 17 00:00:00 2001 From: Felix S Klock II Date: Fri, 17 Apr 2015 18:28:43 +0200 Subject: [PATCH 129/147] aid flow into heavy definitions Added some surrounding text preparing the reader for some denseness. --- _posts/2015-04-17-Enums-match-mutation-and-moves.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/_posts/2015-04-17-Enums-match-mutation-and-moves.md b/_posts/2015-04-17-Enums-match-mutation-and-moves.md index 14686d671..6325f2207 100644 --- a/_posts/2015-04-17-Enums-match-mutation-and-moves.md +++ b/_posts/2015-04-17-Enums-match-mutation-and-moves.md @@ -596,6 +596,8 @@ The only piece left is the `ref`-binding, which is a crucial part of how destructuring bind of L-values works. +First, let us carefully state the meaning of a *non-ref* binding: + * When matching a value of type `T`, an identifier pattern `i` will, on a successful match, *move* the value out of the original input and into `i`. Thus we can always conclude in such a case that `i` has type @@ -612,7 +614,9 @@ Thus, the bindings of `payload` in `tree_weight_v2` both have type `i32`; the `i32` type implements `Copy`, so the weight is copied into `payload` in both arms. -* However, when matching a value of type `T`, a `ref`-pattern `ref i` +Now we are ready to state what a ref-binding is: + +* When matching an L-value of type `T`, a `ref`-pattern `ref i` will, on a successful match, merely *borrow* a reference into the matched data. In other words, a successful `ref i` match of a value of type `T` will imply that `i` has the type of a *reference* to `T` From 72b60f77e8e1a253a6d28490c931e25d73c62866 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 17 Apr 2015 18:33:51 +0200 Subject: [PATCH 130/147] cleanup presentation of "for further reading" list in conclusion. --- _posts/2015-04-17-Enums-match-mutation-and-moves.md | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/_posts/2015-04-17-Enums-match-mutation-and-moves.md b/_posts/2015-04-17-Enums-match-mutation-and-moves.md index 6325f2207..420212a78 100644 --- a/_posts/2015-04-17-Enums-match-mutation-and-moves.md +++ b/_posts/2015-04-17-Enums-match-mutation-and-moves.md @@ -680,10 +680,15 @@ and expressive power, while static analysis ensures that the resulting programs are safe. For more information -on details that were not covered here, such as -defining new named constants, binding via `ident @ pattern`, -or the potentially subtle difference between `{ let id = -expr; ... }` versus `match expr { id => { ... } }`, consult the Rust +on details that were not covered here, such as: + +* defining new named constants, + +* binding via `ident @ pattern`, or + +* the potentially subtle difference between `{ let id = expr; ... }` versus `match expr { id => { ... } }`, + +consult the Rust [documentation][rust_docs], or quiz our awesome community (in `#rust` on IRC, or in the [user group]). From 380bb24f3993bc0f8aa82af354d8609184d7deed Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 17 Apr 2015 18:36:27 +0200 Subject: [PATCH 131/147] make connection between adts and enums. --- _posts/2015-04-17-Enums-match-mutation-and-moves.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/_posts/2015-04-17-Enums-match-mutation-and-moves.md b/_posts/2015-04-17-Enums-match-mutation-and-moves.md index 420212a78..efa1baa55 100644 --- a/_posts/2015-04-17-Enums-match-mutation-and-moves.md +++ b/_posts/2015-04-17-Enums-match-mutation-and-moves.md @@ -286,7 +286,8 @@ analysis to be exhaustive. [adts]: #algebraic-data-types-and-structural-invariants [Algebraic data types] succinctly describe classes of data and allow one -to encode rich structural invariants. +to encode rich structural invariants. Rust uses `enum` and `struct` +definitions for this purpose. An `enum` type allows one to define mutually-exclusive classes of values. The examples shown above used `enum` for simple symbolic tags, From b498981748f6fc15a9976077e03b4cb5318fb75f Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 17 Apr 2015 18:37:15 +0200 Subject: [PATCH 132/147] avoid saying "definitions define ..." --- _posts/2015-04-17-Enums-match-mutation-and-moves.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_posts/2015-04-17-Enums-match-mutation-and-moves.md b/_posts/2015-04-17-Enums-match-mutation-and-moves.md index efa1baa55..bbe5f0def 100644 --- a/_posts/2015-04-17-Enums-match-mutation-and-moves.md +++ b/_posts/2015-04-17-Enums-match-mutation-and-moves.md @@ -291,7 +291,7 @@ definitions for this purpose. An `enum` type allows one to define mutually-exclusive classes of values. The examples shown above used `enum` for simple symbolic tags, -but in Rust, such definitions can define much richer classes of data. +but in Rust, enums can define much richer classes of data. For example, a binary tree is either a leaf, or an internal node with references to two child trees. Here is one way to encode a tree of From 26f79515254ce50d93e755227a4e70762afafc84 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 17 Apr 2015 18:45:11 +0200 Subject: [PATCH 133/147] hint at the `use self::EnumType::*;` trick. --- _posts/2015-04-17-Enums-match-mutation-and-moves.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/_posts/2015-04-17-Enums-match-mutation-and-moves.md b/_posts/2015-04-17-Enums-match-mutation-and-moves.md index bbe5f0def..112665d84 100644 --- a/_posts/2015-04-17-Enums-match-mutation-and-moves.md +++ b/_posts/2015-04-17-Enums-match-mutation-and-moves.md @@ -683,6 +683,8 @@ programs are safe. For more information on details that were not covered here, such as: +* how to say `Higher` instead of `Answer::Higher` in a pattern, + * defining new named constants, * binding via `ident @ pattern`, or From d50a79d5e46e1181e4ce1dc1bd2679632a705419 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 17 Apr 2015 19:06:47 +0200 Subject: [PATCH 134/147] fix formatting bug. --- _posts/2015-04-17-Enums-match-mutation-and-moves.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_posts/2015-04-17-Enums-match-mutation-and-moves.md b/_posts/2015-04-17-Enums-match-mutation-and-moves.md index 112665d84..f192d4d83 100644 --- a/_posts/2015-04-17-Enums-match-mutation-and-moves.md +++ b/_posts/2015-04-17-Enums-match-mutation-and-moves.md @@ -100,7 +100,7 @@ executable; you can cut-and-paste the code snippets into a file `demo.rs`, compile the file with `--test`, and run the resulting binary to see the tests run.) -Patterns can also match [structured data] (e.g. tuples, slices, user-defined +Patterns can also match [structured data][structured data] (e.g. tuples, slices, user-defined data types) via corresponding patterns. In such patterns, one often binds parts of the input to local variables; those variables can then be used in the result expression. From 149c7f6cdd878a14bcbbd94d13e3a8914dda7f3a Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 17 Apr 2015 19:07:19 +0200 Subject: [PATCH 135/147] remove tabs that snuck in somehow. --- ...15-04-17-Enums-match-mutation-and-moves.md | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/_posts/2015-04-17-Enums-match-mutation-and-moves.md b/_posts/2015-04-17-Enums-match-mutation-and-moves.md index f192d4d83..8b9c7ff8c 100644 --- a/_posts/2015-04-17-Enums-match-mutation-and-moves.md +++ b/_posts/2015-04-17-Enums-match-mutation-and-moves.md @@ -89,9 +89,9 @@ fn suggest_guess(prior_guess: u32, answer: Answer) { #[test] fn demo_suggest_guess() { - suggest_guess(10, Answer::Higher); - suggest_guess(20, Answer::Lower); - suggest_guess(19, Answer::Bingo); + suggest_guess(10, Answer::Higher); + suggest_guess(20, Answer::Lower); + suggest_guess(19, Answer::Bingo); } ``` @@ -176,9 +176,9 @@ fn suggest_guess_smarter(s: GuessState) { #[test] fn demo_guess_state() { - suggest_guess_smarter(GuessState { - guess: 20, answer: Answer::Lower, low: 10, high: 1000 - }); + suggest_guess_smarter(GuessState { + guess: 20, answer: Answer::Lower, low: 10, high: 1000 + }); } ``` @@ -266,9 +266,9 @@ fn suggest_guess_fixed(prior_guess: u32, answer: Answer) { #[test] fn demo_guess_fixed() { - suggest_guess_fixed(10, Answer::Higher); - suggest_guess_fixed(20, Answer::Lower); - suggest_guess_fixed(19, Answer::Bingo); + suggest_guess_fixed(10, Answer::Higher); + suggest_guess_fixed(20, Answer::Lower); + suggest_guess_fixed(19, Answer::Bingo); } ``` @@ -460,13 +460,13 @@ fn sometimes_initialize(input: i32) { let borrowed: &str; // a reference to string data match input { 0...100 => { - // Construct a String on the fly... + // Construct a String on the fly... string = format!("input prints as {}", input); - // ... and then borrow from inside it. + // ... and then borrow from inside it. borrowed = &string[6..]; } _ => { - // String literals are *already* borrowed references + // String literals are *already* borrowed references borrowed = "expected between 0 and 100"; } } @@ -474,9 +474,9 @@ fn sometimes_initialize(input: i32) { // Below would cause compile-time error if uncommented... - // println!("string: {}", string); + // println!("string: {}", string); - // ...namely: error: use of possibly uninitialized variable: `string` + // ...namely: error: use of possibly uninitialized variable: `string` } #[test] From e0ea470625429f8695b9495b7343126d86bd318c Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 17 Apr 2015 19:22:26 +0200 Subject: [PATCH 136/147] fix typo. --- _posts/2015-04-17-Enums-match-mutation-and-moves.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_posts/2015-04-17-Enums-match-mutation-and-moves.md b/_posts/2015-04-17-Enums-match-mutation-and-moves.md index 8b9c7ff8c..bcf6e994e 100644 --- a/_posts/2015-04-17-Enums-match-mutation-and-moves.md +++ b/_posts/2015-04-17-Enums-match-mutation-and-moves.md @@ -321,7 +321,7 @@ is a `Node`, but the compiler statically ensures such checks are done: you cannot accidentally interpret the data of a `Leaf` as if it were a `Node`, nor vice versa. -Here is a function that increments all of the integers in a tree +Here is a function that sums all of the integers in a tree using `match`. ```rust From 165d3183e49fcdba3096e205dffe29203ee63bb5 Mon Sep 17 00:00:00 2001 From: Felix S Klock II Date: Fri, 17 Apr 2015 19:44:13 +0200 Subject: [PATCH 137/147] fix formatting bug github preview differs from our own blog rendering in how it handles bullet lists. --- _posts/2015-04-17-Enums-match-mutation-and-moves.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/_posts/2015-04-17-Enums-match-mutation-and-moves.md b/_posts/2015-04-17-Enums-match-mutation-and-moves.md index bcf6e994e..60ed207ae 100644 --- a/_posts/2015-04-17-Enums-match-mutation-and-moves.md +++ b/_posts/2015-04-17-Enums-match-mutation-and-moves.md @@ -419,8 +419,10 @@ fn test_num_to_ordinal() { The Rust compiler accepts the above program. This is notable because its static analyses ensure both: + * `suffix` is always initialized before we run the `format!` at the end of the function, and + * `suffix` is assigned *at most once* during the function's execution (because if we could assign `suffix` multiple times, the compiler would force us to mark `suffix` as mutable). From 527256b93f43526bdbe6ac518398d198e1278d06 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Fri, 17 Apr 2015 10:55:27 -0700 Subject: [PATCH 138/147] Add header anchors --- _config.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/_config.yml b/_config.yml index ece89c2c2..6581330ec 100644 --- a/_config.yml +++ b/_config.yml @@ -11,5 +11,7 @@ github_username: rust-lang # Build settings highlighter: pygments markdown: redcarpet +redcarpet: + extensions: ["with_toc_data"] root: http://blog.rust-lang.org From 8c399bd06ac06160269016acef8dd362e4ff71cf Mon Sep 17 00:00:00 2001 From: Nick Hamann Date: Tue, 21 Apr 2015 16:25:14 -0500 Subject: [PATCH 139/147] Escape left paren so it isn't interpreted as Markdown link url --- _posts/2014-12-12-1.0-Timeline.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_posts/2014-12-12-1.0-Timeline.md b/_posts/2014-12-12-1.0-Timeline.md index c7b177705..df92779eb 100644 --- a/_posts/2014-12-12-1.0-Timeline.md +++ b/_posts/2014-12-12-1.0-Timeline.md @@ -15,7 +15,7 @@ produce Rust 1.0.0 final at least two cycles afterwards**: * Rust 1.0.0 -- One or more six-week cycles later We talked before about [why Rust is reaching 1.0], and also about the -[6-week train model] (with Nightly, Beta, and Stable channels) that will enable +[6-week train model] \(with Nightly, Beta, and Stable channels) that will enable us to deliver stability without stagnation. This post finishes the story by laying out the transition to this new release model and the stability guarantees it provides. From da8ccafb809198522280814fbd2702f5a5046358 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 23 Apr 2015 12:56:12 -0700 Subject: [PATCH 140/147] FFI and Rust --- _posts/2015-04-24-FFI-and-Rust.md | 265 ++++++++++++++++++++++++++++++ 1 file changed, 265 insertions(+) create mode 100644 _posts/2015-04-24-FFI-and-Rust.md diff --git a/_posts/2015-04-24-FFI-and-Rust.md b/_posts/2015-04-24-FFI-and-Rust.md new file mode 100644 index 000000000..0b4a03bab --- /dev/null +++ b/_posts/2015-04-24-FFI-and-Rust.md @@ -0,0 +1,265 @@ +--- +layout: post +title: "FFI and Rust" +author: Alex Crichton +description: "Zero-cost and safe FFI in Rust" +--- + + +Rust's quest for world domination was never destined to happen overnight, so +Rust needs to be able to interoperate with the existing world just as easily +as it talks to itself. To solve this problem, **Rust lets you communicate with C +APIs at no extra cost while providing strong safety guarantees**. + +This is also referred to as Rust's foreign function interface (FFI) and is the +method by which Rust communicates with other programming languages. Following +Rust's design principles, this is a **zero cost abstraction** where function +calls between Rust and C have identical performance to C function calls. FFI +bindings can also leverage language features such as ownership and borrowing to +provide a **safe interface**. + +In this post we'll explore how to encapsulate unsafe FFI calls to C in safe, +zero-cost abstractions by looking at some examples of interacting with C. +Working with C is, however, just an example, as we'll also see how Rust can +easily talk to languages like Python and Ruby just as seamlessly as C. + +### Talking to C + +First, let's start with an example of calling C code from Rust and then +demonstrate that Rust imposes no additional overhead. Starting off simple, +here's a C program which will simply double all the input it's given: + +```c +int double_input(int input) { + return input * 2; +} +``` + +To call this from Rust, one would write this program: + +```rust +extern crate libc; + +extern { + fn double_input(input: libc::c_int) -> libc::c_int; +} + +fn main() { + let input = 4; + let output = unsafe { double_input(input) }; + println!("{} * 2 = {}", input, output); +} +``` + +And that's it! You can try this out for yourself by [checking out the code on +GitHub][rust2c] and running `cargo run` from that directory. At the source level +we can see that there's no burden in calling an external function, and we'll see +soon that the generated code indeed has no overhead. There are, however, a few +subtle aspects of this Rust program so let's cover each piece in detail. + +[rust2c]: https://github.com/alexcrichton/rust-ffi-examples/tree/master/rust-to-c + +First up we see `extern crate libc`. [This crate][libc] provides many useful +type definitions for FFI bindings when talking with C, and it is necessary +to ensure that both C and Rust agree on the types crossing the language +boundary. + +[libc]: https://crates.io/crates/libc + +This leads us nicely into the next part of the program: + +```rust +extern { + fn double_input(input: libc::c_int) -> libc::c_int; +} +``` + +In Rust this is a **declaration** of an externally available function. You can +think of this along the lines of a C header file. Here's where the compiler +learns about the inputs and outputs of the function, and you can see above that +this matches our definition in C. Next up we have the main body of the program: + +```rust +fn main() { + let input = 4; + let output = unsafe { double_input(input) }; + println!("{} * 2 = {}", input, output); +} +``` + +We see one of the crucial aspects of FFI in Rust here, the `unsafe` block. The +compiler knows nothing about the implementation of `double_input`, so it must +assume that memory unsafety *could* happen in this scenario. This may seem +limiting, but Rust has just the right set of tools to allow consumers to not +worry about `unsafe` (more on this in a moment). + +Now that we've seen how to call a C function from Rust, let's see if we can +verify this claim of zero overhead. Almost all programming languages can call +into C one way or another, but it often comes at a cost with runtime type +conversions or perhaps some language runtime juggling. To get a handle on what +Rust is doing, let's go straight to the assembly code of the above `main` +function's call to `double_input`: + +``` +mov $0x4,%edi +callq 3bc30 +``` + +And as before, that's it! Here we can see that calling a C function from Rust +involves precisely one call instruction after moving the arguments into place, +exactly the same cost as it would be in C. + +### Safe Abstractions + +One of Rust's core design principles is its emphasis on ownership, and FFI is no +exception here. When binding a C library in Rust you not only have the benefit +of 0 overhead, but you are also able to make it *safer* than C can! Bindings +can leverage the ownership and borrowing principles in Rust to codify comments +typically found in a C header about how its API should be used. + +For example, consider a C library for parsing a tarball. This library will +expose functions to read the contents of each file in the tarball, probably +something along the lines of: + +```c +// Gets the data for a file in the tarball at the given index, returning NULL if +// it does not exist. The `size` pointer is filled in with the size of the file +// if successful. +const char *tarball_file_data(tarball_t *tarball, unsigned index, size_t *size); +``` + +This function is implicitly making assumptions about how it can be used, +however, by assuming that the `char*` pointer returned cannot outlive the input +tarball. When bound in Rust, this API might look like this instead: + +```rust +pub struct Tarball { raw: *mut tarball_t } + +impl Tarball { + pub fn file(&self, index: u32) -> Option<&[u8]> { + unsafe { + let mut size = 0; + let data = tarball_file_data(self.raw, index as libc::c_uint, + &mut size); + if data.is_null() { + None + } else { + Some(slice::from_raw_parts(data as *const u8, size as usize)) + } + } + } +} +``` + +Here the `*mut tarball_t` pointer is *owned by* a `Tarball`, so we already have +rich knowledge about the lifetime of the resource. Additionally, the `file` +method returns a **borrowed slice** whose lifetime is connected to the same +lifetime as the source tarball itself. This is Rust's way of indicating that the +returned data cannot outlive the tarball, statically preventing bugs that may be +encountered when just using C. + +A key aspect of the Rust binding here is that it is a safe function! Although it +has an `unsafe` implementation (due to calling an FFI function), this interface +is safe to call and will not cause tough-to-track-down segfaults. And don't +forget, all of this is coming at 0 cost as the raw types in C are representable +in Rust with no extra allocations or overhead. + +### Talking to Rust + +A major feature of Rust is that it does not have a garbage collector or +runtime, and one of the benefits of this is that Rust can be called from C with +no setup at all. This means that the zero overhead FFI not only applies when +Rust calls into C, but also when C calls into Rust! + +Let's take the example above, but reverse the roles of each language. As before, +all the code below is [available on GitHub][c2rust]. First we'll start off with +our Rust code: + +[c2rust]: https://github.com/alexcrichton/rust-ffi-examples/tree/master/c-to-rust + +```rust +#[no_mangle] +pub extern fn double_input(input: i32) -> i32 { + input * 2 +} +``` + +As with the Rust code before, there's not a whole lot here but there are some +subtle aspects in play. First off we've got our function definition with a +`#[no_mangle]` attribute. This instructs the compiler to not mangle the symbol +name for the function `double_input`. Rust employs name mangling similar to C++ +to ensure that libraries do not clash with one another, and this attributes +means that you don't have to guess a symbol name like +`double_input::h485dee7f568bebafeaa` from C. + +Next we've got our function definition, and the most interesting part about +this is the keyword `extern`. This is a specialized form of specifying the [ABI +for a function][abi-fn] which enables the function to be compatible with a C +function call. + +[abi-fn]: http://doc.rust-lang.org/reference.html#extern-functions + +Finally, if you [take a look at the `Cargo.toml`][cargo-toml] you'll see that +this library is not compiled as a normal Rust library (rlib) but instead as a +static archive which Rust calls a 'staticlib'. This enables all the relevant +Rust code to be linked statically into the C program we're about to produce. + +[cargo-toml]: https://github.com/alexcrichton/rust-ffi-examples/blob/master/c-to-rust/Cargo.toml#L8 + +Now that we've got our Rust library squared away, let's write our C program +which will call Rust. + +```c +#include +#include + +extern int32_t double_input(int32_t input); + +int main() { + int input = 4; + int output = double_input(input); + printf("%d * 2 = %d\n", input, output); + return 0; +} +``` + +Here we can see that C, like Rust, needs to declare the `double_input` function +that Rust defined. Other than that though everything is ready to go! If you run +`make` from the [directory on GitHub][c2rust] you'll see these examples getting +compiled and linked together and the final executable should run and print +`4 * 2 = 8`. + +Rust's lack of a garbage collector and runtime enables this seamless transition +from C to Rust. The external C code does not need to perform any setup on Rust's +behalf, making the transition that much cheaper. + +### Beyond C + +Up to now we've seen how FFI in Rust has zero overhead and how we can use Rust's +concept of ownership to write safe bindings to C libraries. If you're not using +C, however, you're still in luck! These features of Rust enable it to also be +called from [Python][py2rust], [Ruby][rb2rust], [Javascript][js2rust], and many +more languages. + +[py2rust]: https://github.com/alexcrichton/rust-ffi-examples/tree/master/python-to-rust +[rb2rust]: https://github.com/alexcrichton/rust-ffi-examples/tree/master/ruby-to-rust +[js2rust]: https://github.com/alexcrichton/rust-ffi-examples/tree/master/node-to-rust + +A common desire for writing C code in these languages is to speed up some +component of a library or application that's performance critical. With the +features of Rust we've seen here, however, Rust is just as suitable for this +sort of usage. One of Rust's first production users, +[Skylight](https://www.skylight.io), was able to improve the performance and +memory usage of their data collection agent almost instantly by just using Rust, +and the Rust code is all published as a Ruby gem. + +Moving from a language like Python and Ruby down to C to optimize performance is +often quite difficult as it's tough to ensure that the program won't crash in a +difficult-to-debug way. Rust, however, not only brings zero cost FFI, but *also* +the same safety guarantees the original source language, enabling this sort of +optimization to happen even more frequently! + +FFI is just one of many tools in the toolbox of Rust, but it's a key component +to Rust's adoption as it allows Rust to seamlessly integrate with existing code +bases today. I'm personally quite excited to see the benefits of Rust reach as +many projects as possible! From f998c8761bada6fc7f44aaf0e4f2e446f64f35c1 Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Fri, 24 Apr 2015 09:10:47 -0700 Subject: [PATCH 141/147] Editing pass --- _posts/2015-04-24-FFI-and-Rust.md | 146 +++++++++++++++++------------- 1 file changed, 83 insertions(+), 63 deletions(-) diff --git a/_posts/2015-04-24-FFI-and-Rust.md b/_posts/2015-04-24-FFI-and-Rust.md index 0b4a03bab..4a53cf582 100644 --- a/_posts/2015-04-24-FFI-and-Rust.md +++ b/_posts/2015-04-24-FFI-and-Rust.md @@ -5,29 +5,31 @@ author: Alex Crichton description: "Zero-cost and safe FFI in Rust" --- - Rust's quest for world domination was never destined to happen overnight, so -Rust needs to be able to interoperate with the existing world just as easily -as it talks to itself. To solve this problem, **Rust lets you communicate with C -APIs at no extra cost while providing strong safety guarantees**. - -This is also referred to as Rust's foreign function interface (FFI) and is the -method by which Rust communicates with other programming languages. Following -Rust's design principles, this is a **zero cost abstraction** where function -calls between Rust and C have identical performance to C function calls. FFI -bindings can also leverage language features such as ownership and borrowing to -provide a **safe interface**. +Rust needs to be able to interoperate with the existing world just as easily as +it talks to itself. In particular, **Rust makes it easy to communicate with C +APIs without overhead, and to leverage its ownership system to provide much +stronger safety guarantees for those APIs at the same time**. + +In more detail, Rust's *foreign function interface* (FFI) is the way that it +communicated with other languages. Following Rust's design principles, the FFI +provides a **zero cost abstraction** where function calls between Rust and C +have identical performance to C function calls. FFI bindings can also leverage +language features such as ownership and borrowing to provide a **safe +interface** that enforces protocols around pointers and other resources. These +protocols usually appear only in the documentation for C APIs -- at best -- but +Rust makes them explicit. In this post we'll explore how to encapsulate unsafe FFI calls to C in safe, -zero-cost abstractions by looking at some examples of interacting with C. -Working with C is, however, just an example, as we'll also see how Rust can -easily talk to languages like Python and Ruby just as seamlessly as C. +zero-cost abstractions. Working with C is, however, just an example; we'll also +see how Rust can easily talk to languages like Python and Ruby just as +seamlessly as with C. -### Talking to C +### Rust talking to C -First, let's start with an example of calling C code from Rust and then -demonstrate that Rust imposes no additional overhead. Starting off simple, -here's a C program which will simply double all the input it's given: +Let's start with a simple example of calling C code from Rust and then +demonstrate that Rust imposes no additional overhead. Here's a C program which +will simply double all the input it's given: ```c int double_input(int input) { @@ -35,7 +37,7 @@ int double_input(int input) { } ``` -To call this from Rust, one would write this program: +To call this from Rust, you might write a program like this: ```rust extern crate libc; @@ -51,18 +53,18 @@ fn main() { } ``` -And that's it! You can try this out for yourself by [checking out the code on -GitHub][rust2c] and running `cargo run` from that directory. At the source level -we can see that there's no burden in calling an external function, and we'll see -soon that the generated code indeed has no overhead. There are, however, a few -subtle aspects of this Rust program so let's cover each piece in detail. +And that's it! You can try this out for yourself by +[checking out the code on GitHub][rust2c] and running `cargo run` from that +directory. **At the source level we can see that there's no burden in calling an +external function beyond stating its signature, and we'll see soon that the +generated code indeed has no overhead, either.** There are, however, a few +subtle aspects of this Rust program, so let's cover each piece in detail. [rust2c]: https://github.com/alexcrichton/rust-ffi-examples/tree/master/rust-to-c -First up we see `extern crate libc`. [This crate][libc] provides many useful -type definitions for FFI bindings when talking with C, and it is necessary -to ensure that both C and Rust agree on the types crossing the language -boundary. +First up we see `extern crate libc`. [The libc crate][libc] provides many useful +type definitions for FFI bindings when talking with C, and it makes it easy to +ensure that both C and Rust agree on the types crossing the language boundary. [libc]: https://crates.io/crates/libc @@ -89,9 +91,12 @@ fn main() { We see one of the crucial aspects of FFI in Rust here, the `unsafe` block. The compiler knows nothing about the implementation of `double_input`, so it must -assume that memory unsafety *could* happen in this scenario. This may seem -limiting, but Rust has just the right set of tools to allow consumers to not -worry about `unsafe` (more on this in a moment). +assume that memory unsafety *could* happen whenever you call a foreign function. +The `unsafe` block is how the programmer takes responsibility for ensuring +safety -- you are promising that the actual call you make will not, in fact, +violate memory safety, and thus that Rust's basic guarantees are upheld. This +may seem limiting, but Rust has just the right set of tools to allow consumers +to not worry about `unsafe` (more on this in a moment). Now that we've seen how to call a C function from Rust, let's see if we can verify this claim of zero overhead. Almost all programming languages can call @@ -111,11 +116,11 @@ exactly the same cost as it would be in C. ### Safe Abstractions -One of Rust's core design principles is its emphasis on ownership, and FFI is no -exception here. When binding a C library in Rust you not only have the benefit -of 0 overhead, but you are also able to make it *safer* than C can! Bindings -can leverage the ownership and borrowing principles in Rust to codify comments -typically found in a C header about how its API should be used. +Most features in Rust tie into its core concept of ownership, and the FFI is no +exception. When binding a C library in Rust you not only have the benefit of zero +overhead, but you are also able to make it *safer* than C can! **Bindings can +leverage the ownership and borrowing principles in Rust to codify comments +typically found in a C header about how its API should be used.** For example, consider a C library for parsing a tarball. This library will expose functions to read the contents of each file in the tarball, probably @@ -151,25 +156,35 @@ impl Tarball { } ``` -Here the `*mut tarball_t` pointer is *owned by* a `Tarball`, so we already have -rich knowledge about the lifetime of the resource. Additionally, the `file` -method returns a **borrowed slice** whose lifetime is connected to the same -lifetime as the source tarball itself. This is Rust's way of indicating that the -returned data cannot outlive the tarball, statically preventing bugs that may be -encountered when just using C. - -A key aspect of the Rust binding here is that it is a safe function! Although it -has an `unsafe` implementation (due to calling an FFI function), this interface -is safe to call and will not cause tough-to-track-down segfaults. And don't -forget, all of this is coming at 0 cost as the raw types in C are representable -in Rust with no extra allocations or overhead. - -### Talking to Rust - -A major feature of Rust is that it does not have a garbage collector or -runtime, and one of the benefits of this is that Rust can be called from C with -no setup at all. This means that the zero overhead FFI not only applies when -Rust calls into C, but also when C calls into Rust! +Here the `*mut tarball_t` pointer is *owned by* a `Tarball`, which is +responsible for any destruction and cleanup. So we already have rich knowledge +about the lifetime of the resource: if you have access to a `Tarball`, you know +that the pointer inside must still be valid. Additionally, the `file` method +returns a **borrowed slice** whose lifetime is implicitly connected to the +lifetime of the source tarball itself (the `&self` argument). This is Rust's way +of indicating that the returned slice can only be used within the lifetime of +the tarball, which in turn means that the slice will always point to valid +memory. Thus, Rust statically prevents dangling pointer bugs that are easy to +make when working directly with C. (If you're not familiar with this kind of +borrowing in Rust, have a look at Yehuda Katz's [blog post] on ownership.) + +[blog post]: http://blog.skylight.io/rust-means-never-having-to-close-a-socket/ + +A key aspect of the Rust binding here is that it is a safe function, meaning +that callers do not have to use `unsafe` blocks to invoke it! Although it has an +`unsafe` *implementation* (due to calling an FFI function), the *interface* uses +borrowing to guarantee that no memory unsafety can occur in any Rust code that +uses it. That is, due to Rust's static checking, it's simply not possible to +cause a segfault using the API on the Rust side. And don't forget, all of this +is coming at zero cost: the raw types in C are representable in Rust with no +extra allocations or overhead. + +### C talking to Rust + +**Despite guaranteeing memory safety, Rust does not have a garbage collector or +runtime, and one of the benefits of this is that Rust code can be called from C +with no setup at all.** This means that the zero overhead FFI not only applies +when Rust calls into C, but also when C calls into Rust! Let's take the example above, but reverse the roles of each language. As before, all the code below is [available on GitHub][c2rust]. First we'll start off with @@ -185,10 +200,10 @@ pub extern fn double_input(input: i32) -> i32 { ``` As with the Rust code before, there's not a whole lot here but there are some -subtle aspects in play. First off we've got our function definition with a +subtle aspects in play. First off, we've labeled our function definition with a `#[no_mangle]` attribute. This instructs the compiler to not mangle the symbol name for the function `double_input`. Rust employs name mangling similar to C++ -to ensure that libraries do not clash with one another, and this attributes +to ensure that libraries do not clash with one another, and this attribute means that you don't have to guess a symbol name like `double_input::h485dee7f568bebafeaa` from C. @@ -245,10 +260,13 @@ more languages. [rb2rust]: https://github.com/alexcrichton/rust-ffi-examples/tree/master/ruby-to-rust [js2rust]: https://github.com/alexcrichton/rust-ffi-examples/tree/master/node-to-rust -A common desire for writing C code in these languages is to speed up some -component of a library or application that's performance critical. With the -features of Rust we've seen here, however, Rust is just as suitable for this -sort of usage. One of Rust's first production users, +When writing code in these languages, you sometimes want to speed up some +component that's performance critical, but in the past this often required +dropping all the way to C, and thereby giving up the memory safety, high-level +abstractions, and ergonomics of these languages. + +The fact that Rust can talk to easily with C, however, means that it is also +viable for this sort of usage. One of Rust's first production users, [Skylight](https://www.skylight.io), was able to improve the performance and memory usage of their data collection agent almost instantly by just using Rust, and the Rust code is all published as a Ruby gem. @@ -256,8 +274,10 @@ and the Rust code is all published as a Ruby gem. Moving from a language like Python and Ruby down to C to optimize performance is often quite difficult as it's tough to ensure that the program won't crash in a difficult-to-debug way. Rust, however, not only brings zero cost FFI, but *also* -the same safety guarantees the original source language, enabling this sort of -optimization to happen even more frequently! +makes it possible to retain the same safety guarantees as the original source +language. In the long run, this should make it much easier for programmers in +these languages to drop down and do some systems programming to squeeze out +critical performance when they need it. FFI is just one of many tools in the toolbox of Rust, but it's a key component to Rust's adoption as it allows Rust to seamlessly integrate with existing code From 805b38fd131880ea2816ac263e418849af962809 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 24 Apr 2015 09:44:21 -0700 Subject: [PATCH 142/147] Call out community libs --- _posts/2015-04-24-FFI-and-Rust.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/_posts/2015-04-24-FFI-and-Rust.md b/_posts/2015-04-24-FFI-and-Rust.md index 4a53cf582..565aeabf9 100644 --- a/_posts/2015-04-24-FFI-and-Rust.md +++ b/_posts/2015-04-24-FFI-and-Rust.md @@ -179,6 +179,19 @@ cause a segfault using the API on the Rust side. And don't forget, all of this is coming at zero cost: the raw types in C are representable in Rust with no extra allocations or overhead. +Rust's amazing community has already built some substantial safe bindings around +existing C libraries, including [OpenSSL][rust-openssl], [libgit2][git2-rs], +[libdispatch][dispatch], [libcurl][curl-rust], [sdl2][sdl2], [Unix APIs][nix], +and [libsodium][sodiumoxide]. + +[rust-openssl]: https://crates.io/crates/openssl +[git2-rs]: https://crates.io/crates/git2 +[curl-rust]: https://crates.io/crates/curl +[dispatch]: https://crates.io/crates/dispatch +[sdl2]: https://crates.io/crates/sdl2 +[nix]: https://crates.io/crates/nix +[sodiumoxide]: https://crates.io/crates/sodiumoxide + ### C talking to Rust **Despite guaranteeing memory safety, Rust does not have a garbage collector or From 3d2d4595f6dc396586b3a933b69ab330da1fc91b Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 24 Apr 2015 09:45:04 -0700 Subject: [PATCH 143/147] Add some dashes --- _posts/2015-04-24-FFI-and-Rust.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/_posts/2015-04-24-FFI-and-Rust.md b/_posts/2015-04-24-FFI-and-Rust.md index 565aeabf9..22351525f 100644 --- a/_posts/2015-04-24-FFI-and-Rust.md +++ b/_posts/2015-04-24-FFI-and-Rust.md @@ -13,7 +13,7 @@ stronger safety guarantees for those APIs at the same time**. In more detail, Rust's *foreign function interface* (FFI) is the way that it communicated with other languages. Following Rust's design principles, the FFI -provides a **zero cost abstraction** where function calls between Rust and C +provides a **zero-cost abstraction** where function calls between Rust and C have identical performance to C function calls. FFI bindings can also leverage language features such as ownership and borrowing to provide a **safe interface** that enforces protocols around pointers and other resources. These @@ -101,7 +101,7 @@ to not worry about `unsafe` (more on this in a moment). Now that we've seen how to call a C function from Rust, let's see if we can verify this claim of zero overhead. Almost all programming languages can call into C one way or another, but it often comes at a cost with runtime type -conversions or perhaps some language runtime juggling. To get a handle on what +conversions or perhaps some language-runtime juggling. To get a handle on what Rust is doing, let's go straight to the assembly code of the above `main` function's call to `double_input`: From 3af8d6a209d50de1b02b13e60e8d1b74e89acd89 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 24 Apr 2015 09:50:19 -0700 Subject: [PATCH 144/147] Close up acknowledgements a bit more --- _posts/2015-04-24-FFI-and-Rust.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/_posts/2015-04-24-FFI-and-Rust.md b/_posts/2015-04-24-FFI-and-Rust.md index 22351525f..f1d694c98 100644 --- a/_posts/2015-04-24-FFI-and-Rust.md +++ b/_posts/2015-04-24-FFI-and-Rust.md @@ -7,7 +7,7 @@ description: "Zero-cost and safe FFI in Rust" Rust's quest for world domination was never destined to happen overnight, so Rust needs to be able to interoperate with the existing world just as easily as -it talks to itself. In particular, **Rust makes it easy to communicate with C +it talks to itself. For this reason, **Rust makes it easy to communicate with C APIs without overhead, and to leverage its ownership system to provide much stronger safety guarantees for those APIs at the same time**. @@ -182,7 +182,9 @@ extra allocations or overhead. Rust's amazing community has already built some substantial safe bindings around existing C libraries, including [OpenSSL][rust-openssl], [libgit2][git2-rs], [libdispatch][dispatch], [libcurl][curl-rust], [sdl2][sdl2], [Unix APIs][nix], -and [libsodium][sodiumoxide]. +and [libsodium][sodiumoxide]. This list is also growing quite rapidly on +[crates.io][crates-io], so your favorite C library may already be bound or will +be bound soon! [rust-openssl]: https://crates.io/crates/openssl [git2-rs]: https://crates.io/crates/git2 @@ -191,6 +193,7 @@ and [libsodium][sodiumoxide]. [sdl2]: https://crates.io/crates/sdl2 [nix]: https://crates.io/crates/nix [sodiumoxide]: https://crates.io/crates/sodiumoxide +[crates-io]: https://crates.io ### C talking to Rust From fb731f5bbe1020dffe6f535182739cb63525af80 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 24 Apr 2015 09:52:35 -0700 Subject: [PATCH 145/147] Rename post title --- ...4-FFI-and-Rust.md => 2015-04-24-Rust-Once-Run-Everywhere.md} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename _posts/{2015-04-24-FFI-and-Rust.md => 2015-04-24-Rust-Once-Run-Everywhere.md} (99%) diff --git a/_posts/2015-04-24-FFI-and-Rust.md b/_posts/2015-04-24-Rust-Once-Run-Everywhere.md similarity index 99% rename from _posts/2015-04-24-FFI-and-Rust.md rename to _posts/2015-04-24-Rust-Once-Run-Everywhere.md index f1d694c98..439b675cb 100644 --- a/_posts/2015-04-24-FFI-and-Rust.md +++ b/_posts/2015-04-24-Rust-Once-Run-Everywhere.md @@ -1,6 +1,6 @@ --- layout: post -title: "FFI and Rust" +title: "Rust Once, Run Everywhere" author: Alex Crichton description: "Zero-cost and safe FFI in Rust" --- From 939bc4348a96bc294827c2e2b20399275affcfca Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 24 Apr 2015 10:16:27 -0700 Subject: [PATCH 146/147] Trim down a paragraph a bit --- _posts/2015-04-24-Rust-Once-Run-Everywhere.md | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/_posts/2015-04-24-Rust-Once-Run-Everywhere.md b/_posts/2015-04-24-Rust-Once-Run-Everywhere.md index 439b675cb..03c0116f5 100644 --- a/_posts/2015-04-24-Rust-Once-Run-Everywhere.md +++ b/_posts/2015-04-24-Rust-Once-Run-Everywhere.md @@ -11,14 +11,14 @@ it talks to itself. For this reason, **Rust makes it easy to communicate with C APIs without overhead, and to leverage its ownership system to provide much stronger safety guarantees for those APIs at the same time**. -In more detail, Rust's *foreign function interface* (FFI) is the way that it -communicated with other languages. Following Rust's design principles, the FFI -provides a **zero-cost abstraction** where function calls between Rust and C -have identical performance to C function calls. FFI bindings can also leverage -language features such as ownership and borrowing to provide a **safe -interface** that enforces protocols around pointers and other resources. These -protocols usually appear only in the documentation for C APIs -- at best -- but -Rust makes them explicit. +To communicate with other languages, Rust provides a *foreign function +interface* (FFI). Following Rust's design principles, the FFI provides a +**zero-cost abstraction** where function calls between Rust and C have identical +performance to C function calls. FFI bindings can also leverage language +features such as ownership and borrowing to provide a **safe interface** that +enforces protocols around pointers and other resources. These protocols usually +appear only in the documentation for C APIs -- at best -- but Rust makes them +explicit. In this post we'll explore how to encapsulate unsafe FFI calls to C in safe, zero-cost abstractions. Working with C is, however, just an example; we'll also @@ -157,16 +157,14 @@ impl Tarball { ``` Here the `*mut tarball_t` pointer is *owned by* a `Tarball`, which is -responsible for any destruction and cleanup. So we already have rich knowledge -about the lifetime of the resource: if you have access to a `Tarball`, you know -that the pointer inside must still be valid. Additionally, the `file` method +responsible for any destruction and cleanup, so we already have rich knowledge +about the lifetime of the tarball's memory. Additionally, the `file` method returns a **borrowed slice** whose lifetime is implicitly connected to the lifetime of the source tarball itself (the `&self` argument). This is Rust's way of indicating that the returned slice can only be used within the lifetime of -the tarball, which in turn means that the slice will always point to valid -memory. Thus, Rust statically prevents dangling pointer bugs that are easy to +the tarball, statically preventing dangling pointer bugs that are easy to make when working directly with C. (If you're not familiar with this kind of -borrowing in Rust, have a look at Yehuda Katz's [blog post] on ownership.) +borrowing in Rust, have a look at Yehuda Katz's [blog post on ownership].) [blog post]: http://blog.skylight.io/rust-means-never-having-to-close-a-socket/ From 83b5b1250e33dffeda2ea6a2ae6b28a664ceabe5 Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Fri, 24 Apr 2015 13:30:03 -0400 Subject: [PATCH 147/147] Add missing link to ownership blog post. --- _posts/2015-04-24-Rust-Once-Run-Everywhere.md | 1 + 1 file changed, 1 insertion(+) diff --git a/_posts/2015-04-24-Rust-Once-Run-Everywhere.md b/_posts/2015-04-24-Rust-Once-Run-Everywhere.md index 03c0116f5..4322af45e 100644 --- a/_posts/2015-04-24-Rust-Once-Run-Everywhere.md +++ b/_posts/2015-04-24-Rust-Once-Run-Everywhere.md @@ -184,6 +184,7 @@ and [libsodium][sodiumoxide]. This list is also growing quite rapidly on [crates.io][crates-io], so your favorite C library may already be bound or will be bound soon! +[blog post on ownership]: http://blog.skylight.io/rust-means-never-having-to-close-a-socket/ [rust-openssl]: https://crates.io/crates/openssl [git2-rs]: https://crates.io/crates/git2 [curl-rust]: https://crates.io/crates/curl