mirror of
https://git.proxmox.com/git/rustc
synced 2025-05-04 20:04:58 +00:00
Imported Upstream version 1.3.0+dfsg1
This commit is contained in:
parent
62682a34a5
commit
c1a9b12dc9
52
AUTHORS.txt
52
AUTHORS.txt
@ -10,6 +10,7 @@ Aaron Turon <aturon@mozilla.com>
|
|||||||
Aaron Weiss <aaronweiss74@gmail.com>
|
Aaron Weiss <aaronweiss74@gmail.com>
|
||||||
Abhishek Chanda <abhishek.becs@gmail.com>
|
Abhishek Chanda <abhishek.becs@gmail.com>
|
||||||
Adam Bozanich <adam.boz@gmail.com>
|
Adam Bozanich <adam.boz@gmail.com>
|
||||||
|
Adam Heins <mail@adamheins.com>
|
||||||
Adam Jacob <adam@opscode.com>
|
Adam Jacob <adam@opscode.com>
|
||||||
Adam Roben <adam@roben.org>
|
Adam Roben <adam@roben.org>
|
||||||
Adam Szkoda <adaszko@gmail.com>
|
Adam Szkoda <adaszko@gmail.com>
|
||||||
@ -17,6 +18,7 @@ Adenilson Cavalcanti <cavalcantii@gmail.com>
|
|||||||
Adolfo Ochagavía <aochagavia92@gmail.com>
|
Adolfo Ochagavía <aochagavia92@gmail.com>
|
||||||
Adrien Brault <adrien.brault@gmail.com>
|
Adrien Brault <adrien.brault@gmail.com>
|
||||||
Adrien Tétar <adri-from-59@hotmail.fr>
|
Adrien Tétar <adri-from-59@hotmail.fr>
|
||||||
|
Agoston Szepessy <agszepp@gmail.com>
|
||||||
Ahmed Charles <ahmedcharles@gmail.com>
|
Ahmed Charles <ahmedcharles@gmail.com>
|
||||||
Aidan Cully <github@aidan.users.panix.com>
|
Aidan Cully <github@aidan.users.panix.com>
|
||||||
Aidan Hobson Sayers <aidanhs@cantab.net>
|
Aidan Hobson Sayers <aidanhs@cantab.net>
|
||||||
@ -44,6 +46,7 @@ Alexei Sholik <alcosholik@gmail.com>
|
|||||||
Alex Gaynor <alex.gaynor@gmail.com>
|
Alex Gaynor <alex.gaynor@gmail.com>
|
||||||
Alexis Beingessner <a.beingessner@gmail.com>
|
Alexis Beingessner <a.beingessner@gmail.com>
|
||||||
Alex Lyon <arcterus@mail.com>
|
Alex Lyon <arcterus@mail.com>
|
||||||
|
Alex Newman <posix4e@gmail.com>
|
||||||
Alex Quach <alex@clinkle.com>
|
Alex Quach <alex@clinkle.com>
|
||||||
Alex Rønne Petersen <alex@lycus.org>
|
Alex Rønne Petersen <alex@lycus.org>
|
||||||
Alex Stokes <r.alex.stokes@gmail.com>
|
Alex Stokes <r.alex.stokes@gmail.com>
|
||||||
@ -73,15 +76,19 @@ Andrew Foote <afoote97@gmail.com>
|
|||||||
Andrew Gallant <jamslam@gmail.com>
|
Andrew Gallant <jamslam@gmail.com>
|
||||||
Andrew Hobden <andrew@hoverbear.org>
|
Andrew Hobden <andrew@hoverbear.org>
|
||||||
Andrew Kensler <andrew@eastfarthing.com>
|
Andrew Kensler <andrew@eastfarthing.com>
|
||||||
|
Andrew Kuchev <0coming.soon@gmail.com>
|
||||||
Andrew Paseltiner <apaseltiner@gmail.com>
|
Andrew Paseltiner <apaseltiner@gmail.com>
|
||||||
Andrew Poelstra <asp11@sfu.ca>
|
Andrew Poelstra <asp11@sfu.ca>
|
||||||
Andrew Seidl <dev@aas.io>
|
Andrew Seidl <dev@aas.io>
|
||||||
Andrew Straw <strawman@astraw.com>
|
Andrew Straw <strawman@astraw.com>
|
||||||
Andrew Wagner <drewm1980@gmail.com>
|
Andrew Wagner <drewm1980@gmail.com>
|
||||||
Andrzej Janik <vosen@vosen.pl>
|
Andrzej Janik <vosen@vosen.pl>
|
||||||
|
Andy Caldwell <andrew.caldwell@metaswitch.com>
|
||||||
|
Andy Grover <agrover@redhat.com>
|
||||||
Angus Lees <gus@inodes.org>
|
Angus Lees <gus@inodes.org>
|
||||||
Anthony Juckel <ajuckel@gmail.com>
|
Anthony Juckel <ajuckel@gmail.com>
|
||||||
Anton Löfgren <anton.lofgren@gmail.com>
|
Anton Löfgren <anton.lofgren@gmail.com>
|
||||||
|
Antti Keränen <detegr@gmail.com>
|
||||||
Aram Visser <aramvisser@gmail.com>
|
Aram Visser <aramvisser@gmail.com>
|
||||||
Arcterus <Arcterus@mail.com>
|
Arcterus <Arcterus@mail.com>
|
||||||
Areski Belaid <areski@gmail.com>
|
Areski Belaid <areski@gmail.com>
|
||||||
@ -93,6 +100,7 @@ Armin Ronacher <armin.ronacher@active-4.com>
|
|||||||
Arpad Borsos <arpad.borsos@googlemail.com>
|
Arpad Borsos <arpad.borsos@googlemail.com>
|
||||||
Artem <artemciy@gmail.com>
|
Artem <artemciy@gmail.com>
|
||||||
Arthur Liao <arthurtw8@gmail.com>
|
Arthur Liao <arthurtw8@gmail.com>
|
||||||
|
arthurprs <arthurprs@gmail.com>
|
||||||
arturo <arturo@openframeworks.cc>
|
arturo <arturo@openframeworks.cc>
|
||||||
Ashok Gautham <ScriptDevil@gmail.com>
|
Ashok Gautham <ScriptDevil@gmail.com>
|
||||||
Augusto Hack <hack.augusto@gmail.com>
|
Augusto Hack <hack.augusto@gmail.com>
|
||||||
@ -134,6 +142,7 @@ Bill Wendling <wendling@apple.com>
|
|||||||
Birunthan Mohanathas <birunthan@mohanathas.com>
|
Birunthan Mohanathas <birunthan@mohanathas.com>
|
||||||
Björn Steinbrink <bsteinbr@gmail.com>
|
Björn Steinbrink <bsteinbr@gmail.com>
|
||||||
blake2-ppc <ulrik.sverdrup@gmail.com>
|
blake2-ppc <ulrik.sverdrup@gmail.com>
|
||||||
|
Blake Loring <Blake.Loring@ig.com>
|
||||||
bluss <bluss>
|
bluss <bluss>
|
||||||
bluss <bluss@users.noreply.github.com>
|
bluss <bluss@users.noreply.github.com>
|
||||||
Boris Egorov <egorov@linux.com>
|
Boris Egorov <egorov@linux.com>
|
||||||
@ -158,6 +167,7 @@ Brian J. Burg <burg@cs.washington.edu>
|
|||||||
Brian Koropoff <bkoropoff@gmail.com>
|
Brian Koropoff <bkoropoff@gmail.com>
|
||||||
Brian Leibig <brian@brianleibig.com>
|
Brian Leibig <brian@brianleibig.com>
|
||||||
Brian Quinlan <brian@sweetapp.com>
|
Brian Quinlan <brian@sweetapp.com>
|
||||||
|
Brody Holden <brody.holden.r@gmail.com>
|
||||||
Bruno de Oliveira Abinader <bruno.d@partner.samsung.com>
|
Bruno de Oliveira Abinader <bruno.d@partner.samsung.com>
|
||||||
Bryan Dunsmore <dunsmoreb@gmail.com>
|
Bryan Dunsmore <dunsmoreb@gmail.com>
|
||||||
Byron Williams <byron@112percent.com>
|
Byron Williams <byron@112percent.com>
|
||||||
@ -189,7 +199,9 @@ Chris Pressey <cpressey@gmail.com>
|
|||||||
Chris Sainty <csainty@hotmail.com>
|
Chris Sainty <csainty@hotmail.com>
|
||||||
Chris Shea <cmshea@gmail.com>
|
Chris Shea <cmshea@gmail.com>
|
||||||
Chris Thorn <chris@thorn.co>
|
Chris Thorn <chris@thorn.co>
|
||||||
|
Christian Persson <saser@live.se>
|
||||||
Christian Stadelmann <dev@genodeftest.de>
|
Christian Stadelmann <dev@genodeftest.de>
|
||||||
|
Christian Weinz <christian@madez.de>
|
||||||
Christoph Burgdorf <christoph.burgdorf@bvsn.org>
|
Christoph Burgdorf <christoph.burgdorf@bvsn.org>
|
||||||
Christopher Bergqvist <spambox0@digitalpoetry.se>
|
Christopher Bergqvist <spambox0@digitalpoetry.se>
|
||||||
Christopher Chambers <chris.chambers@peanutcode.com>
|
Christopher Chambers <chris.chambers@peanutcode.com>
|
||||||
@ -228,6 +240,7 @@ Dan Albert <danalbert@google.com>
|
|||||||
Dan Burkert <dan@danburkert.com>
|
Dan Burkert <dan@danburkert.com>
|
||||||
Dan Callahan <dan.callahan@gmail.com>
|
Dan Callahan <dan.callahan@gmail.com>
|
||||||
Dan Connolly <dckc@madmode.com>
|
Dan Connolly <dckc@madmode.com>
|
||||||
|
Daniel Albert <albert_daniel@t-online.de>
|
||||||
Daniel Brooks <db48x@db48x.net>
|
Daniel Brooks <db48x@db48x.net>
|
||||||
Daniel Fagnan <dnfagnan@gmail.com>
|
Daniel Fagnan <dnfagnan@gmail.com>
|
||||||
Daniel Farina <daniel@fdr.io>
|
Daniel Farina <daniel@fdr.io>
|
||||||
@ -278,6 +291,7 @@ Derek Chiang <derekchiang93@gmail.com>
|
|||||||
Derek Guenther <dguenther9@gmail.com>
|
Derek Guenther <dguenther9@gmail.com>
|
||||||
Derek Harland <derek.harland@finq.co.nz>
|
Derek Harland <derek.harland@finq.co.nz>
|
||||||
dgoon <dgoon@dgoon.net>
|
dgoon <dgoon@dgoon.net>
|
||||||
|
diaphore <diaphore@gmail.com>
|
||||||
Diego Giagio <diego@giagio.com>
|
Diego Giagio <diego@giagio.com>
|
||||||
Diego Ongaro <ongaro@cs.stanford.edu>
|
Diego Ongaro <ongaro@cs.stanford.edu>
|
||||||
Diggory Blake <diggsey@googlemail.com>
|
Diggory Blake <diggsey@googlemail.com>
|
||||||
@ -285,6 +299,7 @@ Diggory Hardy <diggory.hardy@gmail.com>
|
|||||||
Dimitri Krassovski <labria@startika.com>
|
Dimitri Krassovski <labria@startika.com>
|
||||||
Dirk Gadsden <dirk@esherido.com>
|
Dirk Gadsden <dirk@esherido.com>
|
||||||
Dirkjan Bussink <d.bussink@gmail.com>
|
Dirkjan Bussink <d.bussink@gmail.com>
|
||||||
|
Dirkjan Ochtman <dirkjan@ochtman.nl>
|
||||||
Dirk Leifeld <leifeld@posteo.de>
|
Dirk Leifeld <leifeld@posteo.de>
|
||||||
Div Shekhar <div@pagerduty.com>
|
Div Shekhar <div@pagerduty.com>
|
||||||
diwic <diwic@users.noreply.github.com>
|
diwic <diwic@users.noreply.github.com>
|
||||||
@ -319,6 +334,7 @@ Ehsanul Hoque <ehsanul@ehsanul.com>
|
|||||||
Elantsev Serj <elantsev@yandex-team.ru>
|
Elantsev Serj <elantsev@yandex-team.ru>
|
||||||
Eli Friedman <eli.friedman@gmail.com>
|
Eli Friedman <eli.friedman@gmail.com>
|
||||||
eliovir <eliovir@gmail.com>
|
eliovir <eliovir@gmail.com>
|
||||||
|
Eljay <lee@leejeffery.co.uk>
|
||||||
Elliott Slaughter <elliottslaughter@gmail.com>
|
Elliott Slaughter <elliottslaughter@gmail.com>
|
||||||
Elly Fong-Jones <elly@leptoquark.net>
|
Elly Fong-Jones <elly@leptoquark.net>
|
||||||
elszben <notgonna@tellyou>
|
elszben <notgonna@tellyou>
|
||||||
@ -343,6 +359,8 @@ Erik Michaels-Ober <sferik@gmail.com>
|
|||||||
Erik Price <erik.price16@gmail.com>
|
Erik Price <erik.price16@gmail.com>
|
||||||
Erik Rose <erik@mozilla.com>
|
Erik Rose <erik@mozilla.com>
|
||||||
Erwan <erwan.ricq@gmail.com>
|
Erwan <erwan.ricq@gmail.com>
|
||||||
|
Esption <esption@gmail.com>
|
||||||
|
eternaleye <eternaleye@gmail.com>
|
||||||
Etienne Millon <me@emillon.org>
|
Etienne Millon <me@emillon.org>
|
||||||
Eunchong Yu <kroisse@gmail.com>
|
Eunchong Yu <kroisse@gmail.com>
|
||||||
Eunji Jeong <eun-ji.jeong@samsung.com>
|
Eunji Jeong <eun-ji.jeong@samsung.com>
|
||||||
@ -371,6 +389,7 @@ fort <e@mail.com>
|
|||||||
Francisco Souza <f@souza.cc>
|
Francisco Souza <f@souza.cc>
|
||||||
frankamp <frankamp@gmail.com>
|
frankamp <frankamp@gmail.com>
|
||||||
Franklin Chen <franklinchen@franklinchen.com>
|
Franklin Chen <franklinchen@franklinchen.com>
|
||||||
|
Frank McSherry <fmcsherry@me.com>
|
||||||
Franziska Hinkelmann <franziska.hinkelmann@gmail.com>
|
Franziska Hinkelmann <franziska.hinkelmann@gmail.com>
|
||||||
free-Runner <aali07@students.poly.edu>
|
free-Runner <aali07@students.poly.edu>
|
||||||
FuGangqiang <fu_gangqiang@163.com>
|
FuGangqiang <fu_gangqiang@163.com>
|
||||||
@ -392,6 +411,7 @@ Geoff Hill <geoff@geoffhill.org>
|
|||||||
Geoffrey Thomas <geofft@ldpreload.com>
|
Geoffrey Thomas <geofft@ldpreload.com>
|
||||||
Geoffroy Couprie <geo.couprie@gmail.com>
|
Geoffroy Couprie <geo.couprie@gmail.com>
|
||||||
Geoffry Song <goffrie@gmail.com>
|
Geoffry Song <goffrie@gmail.com>
|
||||||
|
Georg Brandl <georg@python.org>
|
||||||
George Papanikolaou <g3orge.app@gmail.com>
|
George Papanikolaou <g3orge.app@gmail.com>
|
||||||
Georges Dubus <georges.dubus@gmail.com>
|
Georges Dubus <georges.dubus@gmail.com>
|
||||||
Germano Gabbianelli <tyrion@users.noreply.github.com>
|
Germano Gabbianelli <tyrion@users.noreply.github.com>
|
||||||
@ -478,6 +498,7 @@ Jan Andersson <jan.andersson@gmail.com>
|
|||||||
Jan Bujak <j@exia.io>
|
Jan Bujak <j@exia.io>
|
||||||
Jan-Erik Rediger <janerik@fnordig.de>
|
Jan-Erik Rediger <janerik@fnordig.de>
|
||||||
Jan Kobler <eng1@koblersystems.de>
|
Jan Kobler <eng1@koblersystems.de>
|
||||||
|
Jan Likar <likar.jan@gmail.com>
|
||||||
Jan Niklas Hasse <jhasse@gmail.com>
|
Jan Niklas Hasse <jhasse@gmail.com>
|
||||||
Jannis Harder <jix@jixco.de>
|
Jannis Harder <jix@jixco.de>
|
||||||
Jannis Redmann <mail@jannisr.de>
|
Jannis Redmann <mail@jannisr.de>
|
||||||
@ -486,6 +507,7 @@ Jarod Liu <liuyuanzhi@gmail.com>
|
|||||||
Jashank Jeremy <jashank@rulingia.com>
|
Jashank Jeremy <jashank@rulingia.com>
|
||||||
Jason Fager <jfager@gmail.com>
|
Jason Fager <jfager@gmail.com>
|
||||||
Jason Orendorff <jorendorff@mozilla.com>
|
Jason Orendorff <jorendorff@mozilla.com>
|
||||||
|
Jason Schein <Jasonschein@gmail.com>
|
||||||
Jason Thompson <jason@jthompson.ca>
|
Jason Thompson <jason@jthompson.ca>
|
||||||
Jason Toffaletti <toffaletti@gmail.com>
|
Jason Toffaletti <toffaletti@gmail.com>
|
||||||
Jason Yeo <jasonyeo88@gmail.com>
|
Jason Yeo <jasonyeo88@gmail.com>
|
||||||
@ -499,6 +521,7 @@ J.C. Moyer <jmoyer1992@gmail.com>
|
|||||||
Jeaye <jeaye@arrownext.com>
|
Jeaye <jeaye@arrownext.com>
|
||||||
Jed Davis <jld@panix.com>
|
Jed Davis <jld@panix.com>
|
||||||
Jed Estep <aje@jhu.edu>
|
Jed Estep <aje@jhu.edu>
|
||||||
|
Jeehoon Kang <jeehoon.kang@sf.snu.ac.kr>
|
||||||
Jeff Balogh <jbalogh@mozilla.com>
|
Jeff Balogh <jbalogh@mozilla.com>
|
||||||
Jeff Belgum <jeffbelgum@gmail.com>
|
Jeff Belgum <jeffbelgum@gmail.com>
|
||||||
Jeff Muizelaar <jmuizelaar@mozilla.com>
|
Jeff Muizelaar <jmuizelaar@mozilla.com>
|
||||||
@ -515,6 +538,8 @@ Jesse Luehrs <doy@tozt.net>
|
|||||||
Jesse Ray <jesse@localhost.localdomain>
|
Jesse Ray <jesse@localhost.localdomain>
|
||||||
Jesse Ruderman <jruderman@gmail.com>
|
Jesse Ruderman <jruderman@gmail.com>
|
||||||
Jessy Diamond Exum <jessy.diamondman@gmail.com>
|
Jessy Diamond Exum <jessy.diamondman@gmail.com>
|
||||||
|
Jesús Espino <jespinog@gmail.com>
|
||||||
|
jethrogb <github@jbeekman.nl>
|
||||||
Jexell <Jexell@users.noreply.github.com>
|
Jexell <Jexell@users.noreply.github.com>
|
||||||
Jihyeok Seo <me@limeburst.net>
|
Jihyeok Seo <me@limeburst.net>
|
||||||
Jihyun Yu <j.yu@navercorp.com>
|
Jihyun Yu <j.yu@navercorp.com>
|
||||||
@ -556,6 +581,7 @@ joliv <joliv@users.noreply.github.com>
|
|||||||
Jonas Hietala <tradet.h@gmail.com>
|
Jonas Hietala <tradet.h@gmail.com>
|
||||||
Jonathan Bailey <jbailey@mozilla.com>
|
Jonathan Bailey <jbailey@mozilla.com>
|
||||||
Jonathan Boyett <jonathan@failingservers.com>
|
Jonathan Boyett <jonathan@failingservers.com>
|
||||||
|
Jonathan Hansford <dangthrimble@hansfords.net>
|
||||||
Jonathan Reem <jonathan.reem@gmail.com>
|
Jonathan Reem <jonathan.reem@gmail.com>
|
||||||
Jonathan S <gereeter@gmail.com>
|
Jonathan S <gereeter@gmail.com>
|
||||||
Jonathan Sternberg <jonathansternberg@gmail.com>
|
Jonathan Sternberg <jonathansternberg@gmail.com>
|
||||||
@ -571,6 +597,7 @@ Jorge Aparicio <japaricious@gmail.com>
|
|||||||
Jorge Israel Peña <jorge.israel.p@gmail.com>
|
Jorge Israel Peña <jorge.israel.p@gmail.com>
|
||||||
Joris Rehm <joris.rehm@wakusei.fr>
|
Joris Rehm <joris.rehm@wakusei.fr>
|
||||||
Jormundir <Chaseph@gmail.com>
|
Jormundir <Chaseph@gmail.com>
|
||||||
|
Jose Narvaez <jnarvaez@zendesk.com>
|
||||||
Joseph Crail <jbcrail@gmail.com>
|
Joseph Crail <jbcrail@gmail.com>
|
||||||
Joseph Martin <pythoner6@gmail.com>
|
Joseph Martin <pythoner6@gmail.com>
|
||||||
Joseph Rushton Wakeling <joe@webdrake.net>
|
Joseph Rushton Wakeling <joe@webdrake.net>
|
||||||
@ -595,6 +622,7 @@ Justin Noah <justinnoah@gmail.com>
|
|||||||
juxiliary <juxiliary@gmail.com>
|
juxiliary <juxiliary@gmail.com>
|
||||||
jxv <joevargas@hush.com>
|
jxv <joevargas@hush.com>
|
||||||
Jyun-Yan You <jyyou.tw@gmail.com>
|
Jyun-Yan You <jyyou.tw@gmail.com>
|
||||||
|
Kagami Sascha Rosylight <saschanaz@outlook.com>
|
||||||
Kang Seonghoon <kang.seonghoon@mearie.org>
|
Kang Seonghoon <kang.seonghoon@mearie.org>
|
||||||
Kasey Carrothers <kaseyc.808@gmail.com>
|
Kasey Carrothers <kaseyc.808@gmail.com>
|
||||||
Keegan McAllister <mcallister.keegan@gmail.com>
|
Keegan McAllister <mcallister.keegan@gmail.com>
|
||||||
@ -613,13 +641,17 @@ Kevin Rauwolf <sweetpea-git@tentacle.net>
|
|||||||
Kevin Walter <kevin.walter.private@googlemail.com>
|
Kevin Walter <kevin.walter.private@googlemail.com>
|
||||||
Kevin Yap <me@kevinyap.ca>
|
Kevin Yap <me@kevinyap.ca>
|
||||||
kgv <mail@kgv.name>
|
kgv <mail@kgv.name>
|
||||||
|
Kieran Hunt <kieran.hunt92@gmail.com>
|
||||||
Kiet Tran <ktt3ja@gmail.com>
|
Kiet Tran <ktt3ja@gmail.com>
|
||||||
Kim Røen <kim@pam.no>
|
Kim Røen <kim@pam.no>
|
||||||
kjpgit <kjpgit@users.noreply.github.com>
|
kjpgit <kjpgit@users.noreply.github.com>
|
||||||
klutzy <klutzytheklutzy@gmail.com>
|
klutzy <klutzytheklutzy@gmail.com>
|
||||||
KokaKiwi <kokakiwi+rust@kokakiwi.net>
|
KokaKiwi <kokakiwi+rust@kokakiwi.net>
|
||||||
korenchkin <korenchkin2@gmail.com>
|
korenchkin <korenchkin2@gmail.com>
|
||||||
|
Kornel Lesiński <kornel@geekhood.net>
|
||||||
Kostas Karachalios <vrinek@me.com>
|
Kostas Karachalios <vrinek@me.com>
|
||||||
|
Kristof Söderström <soderstroff@users.noreply.github.com>
|
||||||
|
krumelmonster <krumelmonster@zoho.com>
|
||||||
Krzysztof Drewniak <krzysdrewniak@gmail.com>
|
Krzysztof Drewniak <krzysdrewniak@gmail.com>
|
||||||
Kubilay Kocak <koobs@users.noreply.github.com>
|
Kubilay Kocak <koobs@users.noreply.github.com>
|
||||||
kulakowski <george.kulakowski@gmail.com>
|
kulakowski <george.kulakowski@gmail.com>
|
||||||
@ -635,6 +667,7 @@ Leah Hanson <astrieanna@gmail.com>
|
|||||||
Lee Aronson <lee@libertad.ucsd.edu>
|
Lee Aronson <lee@libertad.ucsd.edu>
|
||||||
Lee Jeffery <leejeffery@gmail.com>
|
Lee Jeffery <leejeffery@gmail.com>
|
||||||
Lee Wondong <wdlee91@gmail.com>
|
Lee Wondong <wdlee91@gmail.com>
|
||||||
|
Leif Arne Storset <leifarne@storset.net>
|
||||||
LemmingAvalanche <haugsbakk@yahoo.no>
|
LemmingAvalanche <haugsbakk@yahoo.no>
|
||||||
Lennart Kudling <github@kudling.de>
|
Lennart Kudling <github@kudling.de>
|
||||||
Leo Correa <lcorr005@gmail.com>
|
Leo Correa <lcorr005@gmail.com>
|
||||||
@ -664,6 +697,7 @@ madmalik <matthias.tellen@googlemail.com>
|
|||||||
Magnus Auvinen <magnus.auvinen@gmail.com>
|
Magnus Auvinen <magnus.auvinen@gmail.com>
|
||||||
Mahmut Bulut <mahmutbulut0@gmail.com>
|
Mahmut Bulut <mahmutbulut0@gmail.com>
|
||||||
maikklein <maikklein@googlemail.com>
|
maikklein <maikklein@googlemail.com>
|
||||||
|
Makoto Kato <m_kato@ga2.so-net.ne.jp>
|
||||||
Makoto Nakashima <makoto.nksm+github@gmail.com>
|
Makoto Nakashima <makoto.nksm+github@gmail.com>
|
||||||
Manish Goregaokar <manishsmail@gmail.com>
|
Manish Goregaokar <manishsmail@gmail.com>
|
||||||
Manuel Hoffmann <manuel@polythematik.de>
|
Manuel Hoffmann <manuel@polythematik.de>
|
||||||
@ -675,6 +709,7 @@ Margaret Meyerhofer <mmeyerho@andrew.cmu.edu>
|
|||||||
Marijn Haverbeke <marijnh@gmail.com>
|
Marijn Haverbeke <marijnh@gmail.com>
|
||||||
Marin Atanasov Nikolov <dnaeon@gmail.com>
|
Marin Atanasov Nikolov <dnaeon@gmail.com>
|
||||||
Mário Feroldi <thelost-t@live.com>
|
Mário Feroldi <thelost-t@live.com>
|
||||||
|
Mark Buer <mark.buer@booktrack.com>
|
||||||
Mark Lacey <641@rudkx.com>
|
Mark Lacey <641@rudkx.com>
|
||||||
Mark Mossberg <mark.mossberg@gmail.com>
|
Mark Mossberg <mark.mossberg@gmail.com>
|
||||||
Mark Rowe <mrowe@bdash.net.nz>
|
Mark Rowe <mrowe@bdash.net.nz>
|
||||||
@ -699,6 +734,7 @@ Matt Brubeck <mbrubeck@limpet.net>
|
|||||||
Matt Carberry <carberry.matt@gmail.com>
|
Matt Carberry <carberry.matt@gmail.com>
|
||||||
Matt Coffin <mcoffin13@gmail.com>
|
Matt Coffin <mcoffin13@gmail.com>
|
||||||
Matt Cox <mattcoxpdx@gmail.com>
|
Matt Cox <mattcoxpdx@gmail.com>
|
||||||
|
Matt Friedman <matthew.friedman@mu.edu>
|
||||||
Matthew Astley <mca@sanger.ac.uk>
|
Matthew Astley <mca@sanger.ac.uk>
|
||||||
Matthew Auld <matthew.auld@intel.com>
|
Matthew Auld <matthew.auld@intel.com>
|
||||||
Matthew Iselin <matthew@theiselins.net>
|
Matthew Iselin <matthew@theiselins.net>
|
||||||
@ -753,6 +789,7 @@ Mickaël Delahaye <mickael.delahaye@gmail.com>
|
|||||||
Mickaël Raybaud-Roig <raybaudroigm@gmail.com>
|
Mickaël Raybaud-Roig <raybaudroigm@gmail.com>
|
||||||
Mickaël Salaün <mic@digikod.net>
|
Mickaël Salaün <mic@digikod.net>
|
||||||
Mick Koch <kchmck@gmail.com>
|
Mick Koch <kchmck@gmail.com>
|
||||||
|
midinastasurazz <mpavlovsky@gmail.com>
|
||||||
Mihnea Dobrescu-Balaur <mihnea@linux.com>
|
Mihnea Dobrescu-Balaur <mihnea@linux.com>
|
||||||
Mike Boutin <mike.boutin@gmail.com>
|
Mike Boutin <mike.boutin@gmail.com>
|
||||||
Mike Dilger <mike@efx.co.nz>
|
Mike Dilger <mike@efx.co.nz>
|
||||||
@ -762,6 +799,7 @@ Mike Robinson <mikeprobinsonuk@gmail.com>
|
|||||||
Mike Sampson <mike@sambodata.com>
|
Mike Sampson <mike@sambodata.com>
|
||||||
Mikhail Zabaluev <mikhail.zabaluev@gmail.com>
|
Mikhail Zabaluev <mikhail.zabaluev@gmail.com>
|
||||||
Mikko Perttunen <cyndis@kapsi.fi>
|
Mikko Perttunen <cyndis@kapsi.fi>
|
||||||
|
mitaa <mitaa.ceb@gmail.com>
|
||||||
mitchmindtree <mitchell.nordine@gmail.com>
|
mitchmindtree <mitchell.nordine@gmail.com>
|
||||||
Mohammed Attia <skeuomorf@gmail.com>
|
Mohammed Attia <skeuomorf@gmail.com>
|
||||||
moonglum <moonglum@moonbeamlabs.com>
|
moonglum <moonglum@moonbeamlabs.com>
|
||||||
@ -795,6 +833,7 @@ Nick Howell <howellnick@gmail.com>
|
|||||||
Nick Platt <platt.nicholas@gmail.com>
|
Nick Platt <platt.nicholas@gmail.com>
|
||||||
Nick Sarten <gen.battle@gmail.com>
|
Nick Sarten <gen.battle@gmail.com>
|
||||||
Nicolas Silva <nical.silva@gmail.com>
|
Nicolas Silva <nical.silva@gmail.com>
|
||||||
|
Nicolette Verlinden <nicole@nicole.moe>
|
||||||
Niels Egberts <git@nielsegberts.nl>
|
Niels Egberts <git@nielsegberts.nl>
|
||||||
Niels langager Ellegaard <niels.ellegaard@gmail.com>
|
Niels langager Ellegaard <niels.ellegaard@gmail.com>
|
||||||
Nif Ward <nif.ward@gmail.com>
|
Nif Ward <nif.ward@gmail.com>
|
||||||
@ -814,7 +853,6 @@ Oak <White-Oak@users.noreply.github.com>
|
|||||||
OGINO Masanori <masanori.ogino@gmail.com>
|
OGINO Masanori <masanori.ogino@gmail.com>
|
||||||
OlegTsyba <idethrone1@gmail.com>
|
OlegTsyba <idethrone1@gmail.com>
|
||||||
Oliver Schneider <git1984941651981@oli-obk.de>
|
Oliver Schneider <git1984941651981@oli-obk.de>
|
||||||
Oliver Schneider <github6541940@oli-obk.de>
|
|
||||||
Olivier Saut <osaut@airpost.net>
|
Olivier Saut <osaut@airpost.net>
|
||||||
olivren <o.renaud@gmx.fr>
|
olivren <o.renaud@gmx.fr>
|
||||||
Olle Jonsson <olle.jonsson@gmail.com>
|
Olle Jonsson <olle.jonsson@gmail.com>
|
||||||
@ -849,6 +887,7 @@ Paul Quint <DrKwint@gmail.com>
|
|||||||
Paul Stansifer <paul.stansifer@gmail.com>
|
Paul Stansifer <paul.stansifer@gmail.com>
|
||||||
Paul Woolcock <pwoolcoc+github@gmail.com>
|
Paul Woolcock <pwoolcoc+github@gmail.com>
|
||||||
Pavel Panchekha <me@pavpanchekha.com>
|
Pavel Panchekha <me@pavpanchekha.com>
|
||||||
|
Pavel Pravosud <pavel@pravosud.com>
|
||||||
Pawel Olzacki <p.olzacki2@samsung.com>
|
Pawel Olzacki <p.olzacki2@samsung.com>
|
||||||
Pedro Larroy <pedro.larroy@here.com>
|
Pedro Larroy <pedro.larroy@here.com>
|
||||||
Peer Aramillo Irizar <peer.aramillo.irizar@gmail.com>
|
Peer Aramillo Irizar <peer.aramillo.irizar@gmail.com>
|
||||||
@ -885,6 +924,7 @@ Q.P.Liu <qpliu@yahoo.com>
|
|||||||
qwitwa <qwitwa@gmail.com>
|
qwitwa <qwitwa@gmail.com>
|
||||||
Rafael Ávila de Espíndola <respindola@mozilla.com>
|
Rafael Ávila de Espíndola <respindola@mozilla.com>
|
||||||
Rahul Horé <hore.rahul@gmail.com>
|
Rahul Horé <hore.rahul@gmail.com>
|
||||||
|
Ralf Jung <post@ralfj.de>
|
||||||
Ralph Bodenner <rkbodenner+github@gmail.com>
|
Ralph Bodenner <rkbodenner+github@gmail.com>
|
||||||
Ralph Giles <giles@thaumas.net>
|
Ralph Giles <giles@thaumas.net>
|
||||||
Ramkumar Ramachandra <artagnon@gmail.com>
|
Ramkumar Ramachandra <artagnon@gmail.com>
|
||||||
@ -940,6 +980,7 @@ Russell McClellan <russell.mcclellan@gmail.com>
|
|||||||
Ruud van Asseldonk <dev@veniogames.com>
|
Ruud van Asseldonk <dev@veniogames.com>
|
||||||
Ryan Levick <ryan@6wunderkinder.com>
|
Ryan Levick <ryan@6wunderkinder.com>
|
||||||
Ryan Mulligan <ryan@ryantm.com>
|
Ryan Mulligan <ryan@ryantm.com>
|
||||||
|
Ryan Pendleton <me@ryanp.me>
|
||||||
Ryan Prichard <ryan.prichard@gmail.com>
|
Ryan Prichard <ryan.prichard@gmail.com>
|
||||||
Ryan Riginding <marc.riginding@gmail.com>
|
Ryan Riginding <marc.riginding@gmail.com>
|
||||||
Ryan Scheel <ryan.havvy@gmail.com>
|
Ryan Scheel <ryan.havvy@gmail.com>
|
||||||
@ -958,7 +999,7 @@ Santiago Rodriguez <sanrodari@gmail.com>
|
|||||||
Saurabh Anand <saurabhanandiit@gmail.com>
|
Saurabh Anand <saurabhanandiit@gmail.com>
|
||||||
Scott Jenkins <scottdjwales@gmail.com>
|
Scott Jenkins <scottdjwales@gmail.com>
|
||||||
Scott Lawrence <bytbox@gmail.com>
|
Scott Lawrence <bytbox@gmail.com>
|
||||||
Scott Olson <scott@scott-olson.org>
|
Scott Olson <scott@solson.me>
|
||||||
Sean Bowe <ewillbefull@gmail.com>
|
Sean Bowe <ewillbefull@gmail.com>
|
||||||
Sean Chalmers <sclhiannan@gmail.com>
|
Sean Chalmers <sclhiannan@gmail.com>
|
||||||
Sean Collins <sean@cllns.com>
|
Sean Collins <sean@cllns.com>
|
||||||
@ -1024,6 +1065,7 @@ Subhash Bhushan <subhash.bhushan@kaybus.com>
|
|||||||
sumito3478 <sumito3478@gmail.com>
|
sumito3478 <sumito3478@gmail.com>
|
||||||
Swaroop C H <swaroop@swaroopch.com>
|
Swaroop C H <swaroop@swaroopch.com>
|
||||||
Sylvestre Ledru <sylvestre@debian.org>
|
Sylvestre Ledru <sylvestre@debian.org>
|
||||||
|
Taliesin Beynon <taliesinb@wolfram.com>
|
||||||
Tamir Duberstein <tamird@gmail.com>
|
Tamir Duberstein <tamird@gmail.com>
|
||||||
Tamir Duberstein <tamird@squareup.com>
|
Tamir Duberstein <tamird@squareup.com>
|
||||||
Taras Shpot <mrshpot@gmail.com>
|
Taras Shpot <mrshpot@gmail.com>
|
||||||
@ -1045,6 +1087,7 @@ Thomas Jespersen <laumann.thomas@gmail.com>
|
|||||||
Thomas Karpiniec <tk@1.21jiggawatts.net>
|
Thomas Karpiniec <tk@1.21jiggawatts.net>
|
||||||
Tiago Nobrega <tigarmo@gmail.com>
|
Tiago Nobrega <tigarmo@gmail.com>
|
||||||
Tibor Benke <ihrwein@gmail.com>
|
Tibor Benke <ihrwein@gmail.com>
|
||||||
|
Ticki <Ticki@users.noreply.github.com>
|
||||||
Till Hoeppner <till@hoeppner.ws>
|
Till Hoeppner <till@hoeppner.ws>
|
||||||
Tim Brooks <brooks@cern.ch>
|
Tim Brooks <brooks@cern.ch>
|
||||||
Tim Chevalier <chevalier@alum.wellesley.edu>
|
Tim Chevalier <chevalier@alum.wellesley.edu>
|
||||||
@ -1086,7 +1129,7 @@ Tyler Bindon <martica@martica.org>
|
|||||||
Tyler Thrailkill <tylerbthrailkill@gmail.com>
|
Tyler Thrailkill <tylerbthrailkill@gmail.com>
|
||||||
tynopex <tynopex@users.noreply.github.com>
|
tynopex <tynopex@users.noreply.github.com>
|
||||||
Ty Overby <ty@pre-alpha.com>
|
Ty Overby <ty@pre-alpha.com>
|
||||||
Ulrik Sverdrup <root@localhost>
|
Ulrik Sverdrup <bluss@users.noreply.github.com>
|
||||||
Ulysse Carion <ulysse@ulysse.io>
|
Ulysse Carion <ulysse@ulysse.io>
|
||||||
User Jyyou <jyyou@plaslab.cs.nctu.edu.tw>
|
User Jyyou <jyyou@plaslab.cs.nctu.edu.tw>
|
||||||
Utkarsh Kukreti <utkarshkukreti@gmail.com>
|
Utkarsh Kukreti <utkarshkukreti@gmail.com>
|
||||||
@ -1102,6 +1145,7 @@ Vijay Korapaty <rust@korapaty.com>
|
|||||||
Viktor Dahl <pazaconyoman@gmail.com>
|
Viktor Dahl <pazaconyoman@gmail.com>
|
||||||
ville-h <ville3.14159@gmail.com>
|
ville-h <ville3.14159@gmail.com>
|
||||||
Vincent Belliard <vincent@famillebelliard.fr>
|
Vincent Belliard <vincent@famillebelliard.fr>
|
||||||
|
Vincent Bernat <vincent@bernat.im>
|
||||||
Vinzent Steinberg <Vinzent.Steinberg@gmail.com>
|
Vinzent Steinberg <Vinzent.Steinberg@gmail.com>
|
||||||
Virgile Andreani <virgile.andreani@anbuco.fr>
|
Virgile Andreani <virgile.andreani@anbuco.fr>
|
||||||
visualfc <visualfc@gmail.com>
|
visualfc <visualfc@gmail.com>
|
||||||
@ -1109,6 +1153,7 @@ Vitali Haravy <HumaneProgrammer@gmail.com>
|
|||||||
Vivek Galatage <vivekgalatage@gmail.com>
|
Vivek Galatage <vivekgalatage@gmail.com>
|
||||||
Vladimir Matveev <vladimir.matweev@gmail.com>
|
Vladimir Matveev <vladimir.matweev@gmail.com>
|
||||||
Vladimir Pouzanov <farcaller@gmail.com>
|
Vladimir Pouzanov <farcaller@gmail.com>
|
||||||
|
Vladimir Rutsky <rutsky@users.noreply.github.com>
|
||||||
Vladimir Smola <smola.vladimir@gmail.com>
|
Vladimir Smola <smola.vladimir@gmail.com>
|
||||||
Vojtech Kral <vojtech@kral.hk>
|
Vojtech Kral <vojtech@kral.hk>
|
||||||
Volker Mische <volker.mische@gmail.com>
|
Volker Mische <volker.mische@gmail.com>
|
||||||
@ -1125,6 +1170,7 @@ Wilfred Hughes <me@wilfred.me.uk>
|
|||||||
Will Andrews <will@firepipe.net>
|
Will Andrews <will@firepipe.net>
|
||||||
Will Engler <engler.will@gmail.com>
|
Will Engler <engler.will@gmail.com>
|
||||||
Will Hipschman <whipsch@gmail.com>
|
Will Hipschman <whipsch@gmail.com>
|
||||||
|
William Throwe <wtt6@cornell.edu>
|
||||||
William Ting <io@williamting.com>
|
William Ting <io@williamting.com>
|
||||||
Willson Mock <willson.mock@gmail.com>
|
Willson Mock <willson.mock@gmail.com>
|
||||||
Will <will@glozer.net>
|
Will <will@glozer.net>
|
||||||
|
@ -83,6 +83,21 @@ feature. We use the 'fork and pull' model described there.
|
|||||||
|
|
||||||
Please make pull requests against the `master` branch.
|
Please make pull requests against the `master` branch.
|
||||||
|
|
||||||
|
Compiling all of `make check` can take a while. When testing your pull request,
|
||||||
|
consider using one of the more specialized `make` targets to cut down on the
|
||||||
|
amount of time you have to wait. You need to have built the compiler at least
|
||||||
|
once before running these will work, but that’s only one full build rather than
|
||||||
|
one each time.
|
||||||
|
|
||||||
|
$ make -j8 rustc-stage1 && make check-stage1
|
||||||
|
|
||||||
|
is one such example, which builds just `rustc`, and then runs the tests. If
|
||||||
|
you’re adding something to the standard library, try
|
||||||
|
|
||||||
|
$ make -j8 check-stage1-std NO_REBUILD=1
|
||||||
|
|
||||||
|
This will not rebuild the compiler, but will run the tests.
|
||||||
|
|
||||||
All pull requests are reviewed by another person. We have a bot,
|
All pull requests are reviewed by another person. We have a bot,
|
||||||
@rust-highfive, that will automatically assign a random person to review your
|
@rust-highfive, that will automatically assign a random person to review your
|
||||||
request.
|
request.
|
||||||
@ -108,14 +123,18 @@ will run all the tests on every platform we support. If it all works out,
|
|||||||
|
|
||||||
[merge-queue]: http://buildbot.rust-lang.org/homu/queue/rust
|
[merge-queue]: http://buildbot.rust-lang.org/homu/queue/rust
|
||||||
|
|
||||||
|
Speaking of tests, Rust has a comprehensive test suite. More information about
|
||||||
|
it can be found
|
||||||
|
[here](https://github.com/rust-lang/rust-wiki-backup/blob/master/Note-testsuite.md).
|
||||||
|
|
||||||
## Writing Documentation
|
## Writing Documentation
|
||||||
|
|
||||||
Documentation improvements are very welcome. The source of `doc.rust-lang.org`
|
Documentation improvements are very welcome. The source of `doc.rust-lang.org`
|
||||||
is located in `src/doc` in the tree, and standard API documentation is generated
|
is located in `src/doc` in the tree, and standard API documentation is generated
|
||||||
from the source code itself.
|
from the source code itself.
|
||||||
|
|
||||||
Documentation pull requests function in the same as other pull requests, though
|
Documentation pull requests function in the same way as other pull requests,
|
||||||
you may see a slightly different form of `r+`:
|
though you may see a slightly different form of `r+`:
|
||||||
|
|
||||||
@bors: r+ 38fe8d2 rollup
|
@bors: r+ 38fe8d2 rollup
|
||||||
|
|
||||||
|
@ -62,6 +62,7 @@
|
|||||||
# * tidy-basic - show file / line stats
|
# * tidy-basic - show file / line stats
|
||||||
# * tidy-errors - show the highest rustc error code
|
# * tidy-errors - show the highest rustc error code
|
||||||
# * tidy-features - show the status of language and lib features
|
# * tidy-features - show the status of language and lib features
|
||||||
|
# * rustc-stage$(stage) - Only build up to a specific stage
|
||||||
#
|
#
|
||||||
# Then mix in some of these environment variables to harness the
|
# Then mix in some of these environment variables to harness the
|
||||||
# ultimate power of The Rust Build System.
|
# ultimate power of The Rust Build System.
|
||||||
@ -90,7 +91,7 @@
|
|||||||
#
|
#
|
||||||
# # Rust recipes for build system success
|
# # Rust recipes for build system success
|
||||||
#
|
#
|
||||||
# // Modifying libstd? Use this comment to run unit tests just on your change
|
# // Modifying libstd? Use this command to run unit tests just on your change
|
||||||
# make check-stage1-std NO_REBUILD=1 NO_BENCH=1
|
# make check-stage1-std NO_REBUILD=1 NO_BENCH=1
|
||||||
#
|
#
|
||||||
# // Added a run-pass test? Use this to test running your test
|
# // Added a run-pass test? Use this to test running your test
|
||||||
|
@ -4,7 +4,7 @@ Rust is a fast systems programming language that guarantees
|
|||||||
memory safety and offers painless concurrency ([no data races]).
|
memory safety and offers painless concurrency ([no data races]).
|
||||||
It does not employ a garbage collector and has minimal runtime overhead.
|
It does not employ a garbage collector and has minimal runtime overhead.
|
||||||
|
|
||||||
This repo contains the code for `rustc`, the Rust compiler, as well
|
This repo contains the code for the compiler (`rustc`), as well
|
||||||
as standard libraries, tools and documentation for Rust.
|
as standard libraries, tools and documentation for Rust.
|
||||||
|
|
||||||
[no data races]: http://blog.rust-lang.org/2015/04/10/Fearless-Concurrency.html
|
[no data races]: http://blog.rust-lang.org/2015/04/10/Fearless-Concurrency.html
|
||||||
@ -73,7 +73,7 @@ Read ["Installing Rust"] from [The Book].
|
|||||||
```
|
```
|
||||||
|
|
||||||
3. Run `mingw32_shell.bat` or `mingw64_shell.bat` from wherever you installed
|
3. Run `mingw32_shell.bat` or `mingw64_shell.bat` from wherever you installed
|
||||||
MYSY2 (i.e. `C:\msys`), depending on whether you want 32-bit or 64-bit Rust.
|
MSYS2 (i.e. `C:\msys`), depending on whether you want 32-bit or 64-bit Rust.
|
||||||
|
|
||||||
4. Navigate to Rust's source code, configure and build it:
|
4. Navigate to Rust's source code, configure and build it:
|
||||||
|
|
||||||
|
228
RELEASES.md
228
RELEASES.md
@ -1,3 +1,209 @@
|
|||||||
|
Version 1.3.0 (September 2015)
|
||||||
|
==============================
|
||||||
|
|
||||||
|
* ~900 changes, numerous bugfixes
|
||||||
|
|
||||||
|
Highlights
|
||||||
|
----------
|
||||||
|
|
||||||
|
* The [new object lifetime defaults][nold] have been [turned
|
||||||
|
on][nold2] after a cycle of warnings about the change. Now types
|
||||||
|
like `&'a Box<Trait>` (or `&'a Rc<Trait>`, etc) will change from
|
||||||
|
being interpreted as `&'a Box<Trait+'a>` to `&'a
|
||||||
|
Box<Trait+'static>`.
|
||||||
|
* [The Rustonomicon][nom] is a new book in the official documentation
|
||||||
|
that dives into writing unsafe Rust.
|
||||||
|
* The [`Duration`] API, [has been stabilized][ds]. This basic unit of
|
||||||
|
timekeeping is employed by other std APIs, as well as out-of-tree
|
||||||
|
time crates.
|
||||||
|
|
||||||
|
Breaking Changes
|
||||||
|
----------------
|
||||||
|
|
||||||
|
* The [new object lifetime defaults][nold] have been [turned
|
||||||
|
on][nold2] after a cycle of warnings about the change.
|
||||||
|
* There is a known [regression][lr] in how object lifetime elision is
|
||||||
|
interpreted, the proper solution for which is undetermined.
|
||||||
|
* The `#[prelude_import]` attribute, an internal implementation
|
||||||
|
detail, was accidentally stabilized previously. [It has been put
|
||||||
|
behind the `prelude_import` feature gate][pi]. This change is
|
||||||
|
believed to break no existing code.
|
||||||
|
* The behavior of [`size_of_val`][dst1] and [`align_of_val`][dst2] is
|
||||||
|
[more sane for dynamically sized types][dst3]. Code that relied on
|
||||||
|
the previous behavior is thought to be broken.
|
||||||
|
* The `dropck` rules, which checks that destructors can't access
|
||||||
|
destroyed values, [have been updated][dropck] to match the
|
||||||
|
[RFC][dropckrfc]. This fixes some soundness holes, and as such will
|
||||||
|
cause some previously-compiling code to no longer build.
|
||||||
|
|
||||||
|
Language
|
||||||
|
--------
|
||||||
|
|
||||||
|
* The [new object lifetime defaults][nold] have been [turned
|
||||||
|
on][nold2] after a cycle of warnings about the change.
|
||||||
|
* Semicolons may [now follow types and paths in
|
||||||
|
macros](https://github.com/rust-lang/rust/pull/27000).
|
||||||
|
* The behavior of [`size_of_val`][dst1] and [`align_of_val`][dst2] is
|
||||||
|
[more sane for dynamically sized types][dst3]. Code that relied on
|
||||||
|
the previous behavior is not known to exist, and suspected to be
|
||||||
|
broken.
|
||||||
|
* `'static` variables [may now be recursive][st].
|
||||||
|
* `ref` bindings choose between [`Deref`] and [`DerefMut`]
|
||||||
|
implementations correctly.
|
||||||
|
* The `dropck` rules, which checks that destructors can't access
|
||||||
|
destroyed values, [have been updated][dropck] to match the
|
||||||
|
[RFC][dropckrfc].
|
||||||
|
|
||||||
|
Libraries
|
||||||
|
---------
|
||||||
|
|
||||||
|
* The [`Duration`] API, [has been stabilized][ds], as well as the
|
||||||
|
`std::time` module, which presently contains only `Duration`.
|
||||||
|
* `Box<str>` and `Box<[T]>` both implement `Clone`.
|
||||||
|
* The owned C string, [`CString`], implements [`Borrow`] and the
|
||||||
|
borrowed C string, [`CStr`], implements [`ToOwned`]. The two of
|
||||||
|
these allow C strings to be borrowed and cloned in generic code.
|
||||||
|
* [`CStr`] implements [`Debug`].
|
||||||
|
* [`AtomicPtr`] implements [`Debug`].
|
||||||
|
* [`Error`] trait objects [can be downcast to their concrete types][e]
|
||||||
|
in many common configurations, using the [`is`], [`downcast`],
|
||||||
|
[`downcast_ref`] and [`downcast_mut`] methods, similarly to the
|
||||||
|
[`Any`] trait.
|
||||||
|
* Searching for substrings now [employs the two-way algorithm][search]
|
||||||
|
instead of doing a naive search. This gives major speedups to a
|
||||||
|
number of methods, including [`contains`][sc], [`find`][sf],
|
||||||
|
[`rfind`][srf], [`split`][ss]. [`starts_with`][ssw] and
|
||||||
|
[`ends_with`][sew] are also faster.
|
||||||
|
* The performance of `PartialEq` for slices is [much faster][ps].
|
||||||
|
* The [`Hash`] trait offers the default method, [`hash_slice`], which
|
||||||
|
is overridden and optimized by the implementations for scalars.
|
||||||
|
* The [`Hasher`] trait now has a number of specialized `write_*`
|
||||||
|
methods for primitive types, for efficiency.
|
||||||
|
* The I/O-specific error type, [`std::io::Error`][ie], gained a set of
|
||||||
|
methods for accessing the 'inner error', if any: [`get_ref`][iegr],
|
||||||
|
[`get_mut`][iegm], [`into_inner`][ieii]. As well, the implementation
|
||||||
|
of [`std::error::Error::cause`][iec] also delegates to the inner
|
||||||
|
error.
|
||||||
|
* [`process::Child`][pc] gained the [`id`] method, which returns a
|
||||||
|
`u32` representing the platform-specific process identifier.
|
||||||
|
* The [`connect`] method on slices is deprecated, replaced by the new
|
||||||
|
[`join`] method (note that both of these are on the *unstable*
|
||||||
|
[`SliceConcatExt`] trait, but through the magic of the prelude are
|
||||||
|
available to stable code anyway).
|
||||||
|
* The [`Div`] operator is implemented for [`Wrapping`] types.
|
||||||
|
* [`DerefMut` is implemented for `String`][dms].
|
||||||
|
* Performance of SipHash (the default hasher for `HashMap`) is
|
||||||
|
[better for long data][sh].
|
||||||
|
* [`AtomicPtr`] implements [`Send`].
|
||||||
|
* The [`read_to_end`] implementations for [`Stdin`] and [`File`]
|
||||||
|
are now [specialized to use uninitalized buffers for increased
|
||||||
|
performance][rte].
|
||||||
|
* Lifetime parameters of foreign functions [are now resolved
|
||||||
|
properly][f].
|
||||||
|
|
||||||
|
Misc
|
||||||
|
----
|
||||||
|
|
||||||
|
* Rust can now, with some coercion, [produce programs that run on
|
||||||
|
Windows XP][xp], though XP is not considered a supported platform.
|
||||||
|
* Porting Rust on Windows from the GNU toolchain to MSVC continues
|
||||||
|
([1][win1], [2][win2], [3][win3], [4][win4]). It is still not
|
||||||
|
recommended for use in 1.3, though should be fully-functional
|
||||||
|
in the [64-bit 1.4 beta][b14].
|
||||||
|
* On Fedora-based systems installation will [properly configure the
|
||||||
|
dynamic linker][fl].
|
||||||
|
* The compiler gained many new extended error descriptions, which can
|
||||||
|
be accessed with the `--explain` flag.
|
||||||
|
* The `dropck` pass, which checks that destructors can't access
|
||||||
|
destroyed values, [has been rewritten][dropck]. This fixes some
|
||||||
|
soundness holes, and as such will cause some previously-compiling
|
||||||
|
code to no longer build.
|
||||||
|
* `rustc` now uses [LLVM to write archive files where possible][ar].
|
||||||
|
Eventually this will eliminate the compiler's dependency on the ar
|
||||||
|
utility.
|
||||||
|
* Rust has [preliminary support for i686 FreeBSD][fb] (it has long
|
||||||
|
supported FreeBSD on x86_64).
|
||||||
|
* The [`unused_mut`][lum], [`unconditional_recursion`][lur],
|
||||||
|
[`improper_ctypes`][lic], and [`negate_unsigned`][lnu] lints are
|
||||||
|
more strict.
|
||||||
|
* If landing pads are disabled (with `-Z no-landing-pads`), [`panic!`
|
||||||
|
will kill the process instead of leaking][nlp].
|
||||||
|
|
||||||
|
[`Any`]: http://doc.rust-lang.org/nightly/std/any/trait.Any.html
|
||||||
|
[`AtomicPtr`]: http://doc.rust-lang.org/nightly/std/sync/atomic/struct.AtomicPtr.html
|
||||||
|
[`Borrow`]: http://doc.rust-lang.org/nightly/std/borrow/trait.Borrow.html
|
||||||
|
[`CStr`]: http://doc.rust-lang.org/nightly/std/ffi/struct.CStr.html
|
||||||
|
[`CString`]: http://doc.rust-lang.org/nightly/std/ffi/struct.CString.html
|
||||||
|
[`Debug`]: http://doc.rust-lang.org/nightly/std/fmt/trait.Debug.html
|
||||||
|
[`DerefMut`]: http://doc.rust-lang.org/nightly/std/ops/trait.DerefMut.html
|
||||||
|
[`Deref`]: http://doc.rust-lang.org/nightly/std/ops/trait.Deref.html
|
||||||
|
[`Div`]: http://doc.rust-lang.org/nightly/std/ops/trait.Div.html
|
||||||
|
[`Duration`]: http://doc.rust-lang.org/nightly/std/time/struct.Duration.html
|
||||||
|
[`Error`]: http://doc.rust-lang.org/nightly/std/error/trait.Error.html
|
||||||
|
[`File`]: http://doc.rust-lang.org/nightly/std/fs/struct.File.html
|
||||||
|
[`Hash`]: http://doc.rust-lang.org/nightly/std/hash/trait.Hash.html
|
||||||
|
[`Hasher`]: http://doc.rust-lang.org/nightly/std/hash/trait.Hash.html
|
||||||
|
[`Send`]: http://doc.rust-lang.org/nightly/std/marker/trait.Send.html
|
||||||
|
[`SliceConcatExt`]: http://doc.rust-lang.org/nightly/std/slice/trait.SliceConcatExt.html
|
||||||
|
[`Stdin`]: http://doc.rust-lang.org/nightly/std/io/struct.Stdin.html
|
||||||
|
[`ToOwned`]: http://doc.rust-lang.org/nightly/std/borrow/trait.ToOwned.html
|
||||||
|
[`Wrapping`]: http://doc.rust-lang.org/nightly/std/num/struct.Wrapping.html
|
||||||
|
[`connect`]: http://doc.rust-lang.org/nightly/std/slice/trait.SliceConcatExt.html#method.connect
|
||||||
|
[`downcast_mut`]: http://doc.rust-lang.org/nightly/std/error/trait.Error.html#method.downcast_mut
|
||||||
|
[`downcast_ref`]: http://doc.rust-lang.org/nightly/std/error/trait.Error.html#method.downcast_ref
|
||||||
|
[`downcast`]: http://doc.rust-lang.org/nightly/std/error/trait.Error.html#method.downcast
|
||||||
|
[`hash_slice`]: http://doc.rust-lang.org/nightly/std/hash/trait.Hash.html#method.hash_slice
|
||||||
|
[`id`]: http://doc.rust-lang.org/nightly/std/process/struct.Child.html#method.id
|
||||||
|
[`is`]: http://doc.rust-lang.org/nightly/std/error/trait.Error.html#method.is
|
||||||
|
[`join`]: http://doc.rust-lang.org/nightly/std/slice/trait.SliceConcatExt.html#method.join
|
||||||
|
[`read_to_end`]: http://doc.rust-lang.org/nightly/std/io/trait.Read.html#method.read_to_end
|
||||||
|
[ar]: https://github.com/rust-lang/rust/pull/26926
|
||||||
|
[b14]: https://static.rust-lang.org/dist/rust-beta-x86_64-pc-windows-msvc.msi
|
||||||
|
[dms]: https://github.com/rust-lang/rust/pull/26241
|
||||||
|
[dropck]: https://github.com/rust-lang/rust/pull/27261
|
||||||
|
[dropckrfc]: https://github.com/rust-lang/rfcs/blob/master/text/0769-sound-generic-drop.md
|
||||||
|
[ds]: https://github.com/rust-lang/rust/pull/26818
|
||||||
|
[dst1]: http://doc.rust-lang.org/nightly/std/mem/fn.size_of_val.html
|
||||||
|
[dst2]: http://doc.rust-lang.org/nightly/std/mem/fn.align_of_val.html
|
||||||
|
[dst3]: https://github.com/rust-lang/rust/pull/27351
|
||||||
|
[e]: https://github.com/rust-lang/rust/pull/24793
|
||||||
|
[f]: https://github.com/rust-lang/rust/pull/26588
|
||||||
|
[fb]: https://github.com/rust-lang/rust/pull/26959
|
||||||
|
[fl]: https://github.com/rust-lang/rust-installer/pull/41
|
||||||
|
[hs]: http://doc.rust-lang.org/nightly/std/hash/trait.Hash.html#method.hash_slice
|
||||||
|
[ie]: http://doc.rust-lang.org/nightly/std/io/struct.Error.html
|
||||||
|
[iec]: http://doc.rust-lang.org/nightly/std/io/struct.Error.html#method.cause
|
||||||
|
[iegm]: http://doc.rust-lang.org/nightly/std/io/struct.Error.html#method.get_mut
|
||||||
|
[iegr]: http://doc.rust-lang.org/nightly/std/io/struct.Error.html#method.get_ref
|
||||||
|
[ieii]: http://doc.rust-lang.org/nightly/std/io/struct.Error.html#method.into_inner
|
||||||
|
[lic]: https://github.com/rust-lang/rust/pull/26583
|
||||||
|
[lnu]: https://github.com/rust-lang/rust/pull/27026
|
||||||
|
[lr]: https://github.com/rust-lang/rust/issues/27248
|
||||||
|
[lum]: https://github.com/rust-lang/rust/pull/26378
|
||||||
|
[lur]: https://github.com/rust-lang/rust/pull/26783
|
||||||
|
[nlp]: https://github.com/rust-lang/rust/pull/27176
|
||||||
|
[nold2]: https://github.com/rust-lang/rust/pull/27045
|
||||||
|
[nold]: https://github.com/rust-lang/rfcs/blob/master/text/1156-adjust-default-object-bounds.md
|
||||||
|
[nom]: http://doc.rust-lang.org/nightly/nomicon/
|
||||||
|
[pc]: http://doc.rust-lang.org/nightly/std/process/struct.Child.html
|
||||||
|
[pi]: https://github.com/rust-lang/rust/pull/26699
|
||||||
|
[ps]: https://github.com/rust-lang/rust/pull/26884
|
||||||
|
[rte]: https://github.com/rust-lang/rust/pull/26950
|
||||||
|
[sc]: http://doc.rust-lang.org/nightly/std/primitive.str.html#method.contains
|
||||||
|
[search]: https://github.com/rust-lang/rust/pull/26327
|
||||||
|
[sew]: http://doc.rust-lang.org/nightly/std/primitive.str.html#method.ends_with
|
||||||
|
[sf]: http://doc.rust-lang.org/nightly/std/primitive.str.html#method.find
|
||||||
|
[sh]: https://github.com/rust-lang/rust/pull/27280
|
||||||
|
[srf]: http://doc.rust-lang.org/nightly/std/primitive.str.html#method.rfind
|
||||||
|
[ss]: http://doc.rust-lang.org/nightly/std/primitive.str.html#method.split
|
||||||
|
[ssw]: http://doc.rust-lang.org/nightly/std/primitive.str.html#method.starts_with
|
||||||
|
[st]: https://github.com/rust-lang/rust/pull/26630
|
||||||
|
[win1]: https://github.com/rust-lang/rust/pull/26569
|
||||||
|
[win2]: https://github.com/rust-lang/rust/pull/26741
|
||||||
|
[win3]: https://github.com/rust-lang/rust/pull/26741
|
||||||
|
[win4]: https://github.com/rust-lang/rust/pull/27210
|
||||||
|
[xp]: https://github.com/rust-lang/rust/pull/26569
|
||||||
|
|
||||||
Version 1.2.0 (August 2015)
|
Version 1.2.0 (August 2015)
|
||||||
===========================
|
===========================
|
||||||
|
|
||||||
@ -164,17 +370,16 @@ Version 1.1.0 (June 2015)
|
|||||||
Highlights
|
Highlights
|
||||||
----------
|
----------
|
||||||
|
|
||||||
* The [`std::fs` module has been expanded][fs-expand] to expand the set of
|
* The [`std::fs` module has been expanded][fs] to expand the set of
|
||||||
functionality exposed:
|
functionality exposed:
|
||||||
* `DirEntry` now supports optimizations like `file_type` and `metadata` which
|
* `DirEntry` now supports optimizations like `file_type` and `metadata` which
|
||||||
don't incur a syscall on some platforms.
|
don't incur a syscall on some platforms.
|
||||||
* A `symlink_metadata` function has been added.
|
* A `symlink_metadata` function has been added.
|
||||||
* The `fs::Metadata` structure now lowers to its OS counterpart, providing
|
* The `fs::Metadata` structure now lowers to its OS counterpart, providing
|
||||||
access to all underlying information.
|
access to all underlying information.
|
||||||
* The compiler contains extended explanations of many errors. When it
|
* The compiler now contains extended explanations of many errors. When an error
|
||||||
emits such an error it also suggests using the `--explain` flag to
|
with an explanation occurs the compiler suggests using the `--explain` flag
|
||||||
read the extended explanations, which are also [cataloged on the web
|
to read the explanation. Error explanations are also [available online][err-index].
|
||||||
site][err].
|
|
||||||
* Thanks to multiple [improvements][sk] to [type checking][pre], as
|
* Thanks to multiple [improvements][sk] to [type checking][pre], as
|
||||||
well as other work, the time to bootstrap the compiler decreased by
|
well as other work, the time to bootstrap the compiler decreased by
|
||||||
32%.
|
32%.
|
||||||
@ -182,11 +387,11 @@ Highlights
|
|||||||
Libraries
|
Libraries
|
||||||
---------
|
---------
|
||||||
|
|
||||||
* The `str::split_whitespace` method splits a string on unicode
|
* The [`str::split_whitespace`] method splits a string on unicode
|
||||||
whitespace boundaries.
|
whitespace boundaries.
|
||||||
* On both Windows and Unix, new extension traits provide conversion of
|
* On both Windows and Unix, new extension traits provide conversion of
|
||||||
I/O types to and from the underlying system handles. On Unix, these
|
I/O types to and from the underlying system handles. On Unix, these
|
||||||
traits are [`FrowRawFd`] and [`AsRawFd`], on Windows `FromRawHandle`
|
traits are [`FromRawFd`] and [`AsRawFd`], on Windows `FromRawHandle`
|
||||||
and `AsRawHandle`. These are implemented for `File`, `TcpStream`,
|
and `AsRawHandle`. These are implemented for `File`, `TcpStream`,
|
||||||
`TcpListener`, and `UpdSocket`. Further implementations for
|
`TcpListener`, and `UpdSocket`. Further implementations for
|
||||||
`std::process` will be stabilized later.
|
`std::process` will be stabilized later.
|
||||||
@ -238,15 +443,14 @@ Misc
|
|||||||
* [The `drop_with_repr_extern` lint warns about mixing `repr(C)`
|
* [The `drop_with_repr_extern` lint warns about mixing `repr(C)`
|
||||||
with `Drop`][drop].
|
with `Drop`][drop].
|
||||||
|
|
||||||
[`split_whitespace`]: http://doc.rust-lang.org/nightly/std/primitive.str.html#method.split_whitespace
|
[`str::split_whitespace`]: http://doc.rust-lang.org/nightly/std/primitive.str.html#method.split_whitespace
|
||||||
[`Iterator::cloned`]: http://doc.rust-lang.org/nightly/core/iter/trait.Iterator.html#method.cloned
|
|
||||||
[`FromRawFd`]: http://doc.rust-lang.org/nightly/std/os/unix/io/trait.FromRawFd.html
|
[`FromRawFd`]: http://doc.rust-lang.org/nightly/std/os/unix/io/trait.FromRawFd.html
|
||||||
[`AsRawFd`]: http://doc.rust-lang.org/nightly/std/os/unix/io/trait.AsRawFd.html
|
[`AsRawFd`]: http://doc.rust-lang.org/nightly/std/os/unix/io/trait.AsRawFd.html
|
||||||
[`std::os::unix::symlink`]: http://doc.rust-lang.org/nightly/std/os/unix/fs/fn.symlink.html
|
[`std::os::unix::symlink`]: http://doc.rust-lang.org/nightly/std/os/unix/fs/fn.symlink.html
|
||||||
[`IntoIterator`]: http://doc.rust-lang.org/nightly/std/iter/trait.IntoIterator.html
|
[`IntoIterator`]: http://doc.rust-lang.org/nightly/std/iter/trait.IntoIterator.html
|
||||||
[`From`]: http://doc.rust-lang.org/nightly/std/convert/trait.From.html
|
[`From`]: http://doc.rust-lang.org/nightly/std/convert/trait.From.html
|
||||||
[rf]: https://github.com/rust-lang/rust/pull/24491
|
[rf]: https://github.com/rust-lang/rust/pull/24491
|
||||||
[err]: http://doc.rust-lang.org/error-index.html
|
[err-index]: http://doc.rust-lang.org/error-index.html
|
||||||
[sk]: https://github.com/rust-lang/rust/pull/24615
|
[sk]: https://github.com/rust-lang/rust/pull/24615
|
||||||
[pre]: https://github.com/rust-lang/rust/pull/25323
|
[pre]: https://github.com/rust-lang/rust/pull/25323
|
||||||
[file]: https://github.com/rust-lang/rust/pull/24598
|
[file]: https://github.com/rust-lang/rust/pull/24598
|
||||||
@ -409,7 +613,6 @@ Misc
|
|||||||
[sw]: https://github.com/rust-lang/rfcs/blob/master/text/1054-str-words.md
|
[sw]: https://github.com/rust-lang/rfcs/blob/master/text/1054-str-words.md
|
||||||
[th]: https://github.com/rust-lang/rfcs/blob/master/text/0909-move-thread-local-to-std-thread.md
|
[th]: https://github.com/rust-lang/rfcs/blob/master/text/0909-move-thread-local-to-std-thread.md
|
||||||
[send-rfc]: https://github.com/rust-lang/rfcs/blob/master/text/0458-send-improvements.md
|
[send-rfc]: https://github.com/rust-lang/rfcs/blob/master/text/0458-send-improvements.md
|
||||||
[scoped]: http://static.rust-lang.org/doc/master/std/thread/fn.scoped.html
|
|
||||||
[moar-ufcs]: https://github.com/rust-lang/rust/pull/22172
|
[moar-ufcs]: https://github.com/rust-lang/rust/pull/22172
|
||||||
[prim-inherent]: https://github.com/rust-lang/rust/pull/23104
|
[prim-inherent]: https://github.com/rust-lang/rust/pull/23104
|
||||||
[overflow]: https://github.com/rust-lang/rfcs/blob/master/text/0560-integer-overflow.md
|
[overflow]: https://github.com/rust-lang/rfcs/blob/master/text/0560-integer-overflow.md
|
||||||
@ -419,12 +622,10 @@ Misc
|
|||||||
[string-pattern]: https://github.com/rust-lang/rust/pull/22466
|
[string-pattern]: https://github.com/rust-lang/rust/pull/22466
|
||||||
[oibit-final]: https://github.com/rust-lang/rust/pull/21689
|
[oibit-final]: https://github.com/rust-lang/rust/pull/21689
|
||||||
[reflect]: https://github.com/rust-lang/rust/pull/23712
|
[reflect]: https://github.com/rust-lang/rust/pull/23712
|
||||||
[debug-builder]: https://github.com/rust-lang/rfcs/blob/master/text/0640-debug-improvements.md
|
|
||||||
[conversion]: https://github.com/rust-lang/rfcs/pull/529
|
[conversion]: https://github.com/rust-lang/rfcs/pull/529
|
||||||
[num-traits]: https://github.com/rust-lang/rust/pull/23549
|
[num-traits]: https://github.com/rust-lang/rust/pull/23549
|
||||||
[index-value]: https://github.com/rust-lang/rust/pull/23601
|
[index-value]: https://github.com/rust-lang/rust/pull/23601
|
||||||
[dropck]: https://github.com/rust-lang/rfcs/pull/769
|
[dropck]: https://github.com/rust-lang/rfcs/pull/769
|
||||||
[fundamental]: https://github.com/rust-lang/rfcs/pull/1023
|
|
||||||
[ci-compare]: https://gist.github.com/brson/a30a77836fbec057cbee
|
[ci-compare]: https://gist.github.com/brson/a30a77836fbec057cbee
|
||||||
[fn-inherit]: https://github.com/rust-lang/rust/pull/23282
|
[fn-inherit]: https://github.com/rust-lang/rust/pull/23282
|
||||||
[fn-blanket]: https://github.com/rust-lang/rust/pull/23895
|
[fn-blanket]: https://github.com/rust-lang/rust/pull/23895
|
||||||
@ -527,7 +728,6 @@ Version 1.0.0-alpha.2 (February 2015)
|
|||||||
[osstr]: https://github.com/rust-lang/rust/pull/21488
|
[osstr]: https://github.com/rust-lang/rust/pull/21488
|
||||||
[osstr-rfc]: https://github.com/rust-lang/rfcs/blob/master/text/0517-io-os-reform.md
|
[osstr-rfc]: https://github.com/rust-lang/rfcs/blob/master/text/0517-io-os-reform.md
|
||||||
[Self]: https://github.com/rust-lang/rust/pull/22158
|
[Self]: https://github.com/rust-lang/rust/pull/22158
|
||||||
[ufcs]: https://github.com/rust-lang/rust/pull/21077
|
|
||||||
[ufcs-rfc]: https://github.com/rust-lang/rfcs/blob/master/text/0132-ufcs.md
|
[ufcs-rfc]: https://github.com/rust-lang/rfcs/blob/master/text/0132-ufcs.md
|
||||||
[un]: https://github.com/rust-lang/rust/pull/22256
|
[un]: https://github.com/rust-lang/rust/pull/22256
|
||||||
|
|
||||||
|
247
configure
vendored
247
configure
vendored
@ -283,7 +283,7 @@ opt_core() {
|
|||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
else
|
else
|
||||||
if [ ! -z "$META" ]
|
if [ -n "$META" ]
|
||||||
then
|
then
|
||||||
OP="$OP=<$META>"
|
OP="$OP=<$META>"
|
||||||
fi
|
fi
|
||||||
@ -317,12 +317,23 @@ envopt() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# If script or environment provided a value, save it.
|
# If script or environment provided a value, save it.
|
||||||
if [ ! -z "$VV" ]
|
if [ -n "$VV" ]
|
||||||
then
|
then
|
||||||
putvar $V
|
putvar $V
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enable_if_not_disabled() {
|
||||||
|
local OP=$1
|
||||||
|
local UOP=$(echo $OP | tr '[:lower:]' '[:upper:]' | tr '\-' '\_')
|
||||||
|
local ENAB_V="CFG_ENABLE_$UOP"
|
||||||
|
local EXPLICITLY_DISABLED="CFG_DISABLE_${UOP}_PROVIDED"
|
||||||
|
eval VV=\$$EXPLICITLY_DISABLED
|
||||||
|
if [ -z "$VV" ]; then
|
||||||
|
eval $ENAB_V=1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
to_llvm_triple() {
|
to_llvm_triple() {
|
||||||
case $1 in
|
case $1 in
|
||||||
i686-w64-mingw32) echo i686-pc-windows-gnu ;;
|
i686-w64-mingw32) echo i686-pc-windows-gnu ;;
|
||||||
@ -405,6 +416,10 @@ case $CFG_OSTYPE in
|
|||||||
CFG_OSTYPE=unknown-openbsd
|
CFG_OSTYPE=unknown-openbsd
|
||||||
;;
|
;;
|
||||||
|
|
||||||
|
NetBSD)
|
||||||
|
CFG_OSTYPE=unknown-netbsd
|
||||||
|
;;
|
||||||
|
|
||||||
Darwin)
|
Darwin)
|
||||||
CFG_OSTYPE=apple-darwin
|
CFG_OSTYPE=apple-darwin
|
||||||
;;
|
;;
|
||||||
@ -586,7 +601,9 @@ valopt llvm-root "" "set LLVM root"
|
|||||||
valopt python "" "set path to python"
|
valopt python "" "set path to python"
|
||||||
valopt jemalloc-root "" "set directory where libjemalloc_pic.a is located"
|
valopt jemalloc-root "" "set directory where libjemalloc_pic.a is located"
|
||||||
valopt build "${DEFAULT_BUILD}" "GNUs ./configure syntax LLVM build triple"
|
valopt build "${DEFAULT_BUILD}" "GNUs ./configure syntax LLVM build triple"
|
||||||
valopt android-cross-path "/opt/ndk_standalone" "Android NDK standalone path"
|
valopt android-cross-path "/opt/ndk_standalone" "Android NDK standalone path (deprecated)"
|
||||||
|
valopt arm-linux-androideabi-ndk "" "arm-linux-androideabi NDK standalone path"
|
||||||
|
valopt aarch64-linux-android-ndk "" "aarch64-linux-android NDK standalone path"
|
||||||
valopt release-channel "dev" "the name of the release channel to build"
|
valopt release-channel "dev" "the name of the release channel to build"
|
||||||
valopt musl-root "/usr/local" "MUSL root installation directory"
|
valopt musl-root "/usr/local" "MUSL root installation directory"
|
||||||
|
|
||||||
@ -667,10 +684,12 @@ if [ -n "$CFG_ENABLE_DEBUG" ]; then
|
|||||||
CFG_DISABLE_OPTIMIZE=1
|
CFG_DISABLE_OPTIMIZE=1
|
||||||
CFG_DISABLE_OPTIMIZE_CXX=1
|
CFG_DISABLE_OPTIMIZE_CXX=1
|
||||||
fi
|
fi
|
||||||
CFG_ENABLE_DEBUG_ASSERTIONS=1
|
|
||||||
CFG_ENABLE_DEBUG_JEMALLOC=1
|
# Set following variables to 1 unless setting already provided
|
||||||
CFG_ENABLE_DEBUGINFO=1
|
enable_if_not_disabled debug-assertions
|
||||||
CFG_ENABLE_LLVM_ASSERTIONS=1
|
enable_if_not_disabled debug-jemalloc
|
||||||
|
enable_if_not_disabled debuginfo
|
||||||
|
enable_if_not_disabled llvm-assertions
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# OK, now write the debugging options
|
# OK, now write the debugging options
|
||||||
@ -750,7 +769,7 @@ probe CFG_LLDB lldb
|
|||||||
# On MacOS X, invoking `javac` pops up a dialog if the JDK is not
|
# On MacOS X, invoking `javac` pops up a dialog if the JDK is not
|
||||||
# installed. Since `javac` is only used if `antlr4` is available,
|
# installed. Since `javac` is only used if `antlr4` is available,
|
||||||
# probe for it only in this case.
|
# probe for it only in this case.
|
||||||
if [ ! -z "$CFG_ANTLR4" ]
|
if [ -n "$CFG_ANTLR4" ]
|
||||||
then
|
then
|
||||||
probe CFG_JAVAC javac
|
probe CFG_JAVAC javac
|
||||||
fi
|
fi
|
||||||
@ -769,14 +788,14 @@ then
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ ! -z "$CFG_GDB" ]
|
if [ -n "$CFG_GDB" ]
|
||||||
then
|
then
|
||||||
# Store GDB's version
|
# Store GDB's version
|
||||||
CFG_GDB_VERSION=$($CFG_GDB --version 2>/dev/null | head -1)
|
CFG_GDB_VERSION=$($CFG_GDB --version 2>/dev/null | head -1)
|
||||||
putvar CFG_GDB_VERSION
|
putvar CFG_GDB_VERSION
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ ! -z "$CFG_LLDB" ]
|
if [ -n "$CFG_LLDB" ]
|
||||||
then
|
then
|
||||||
# Store LLDB's version
|
# Store LLDB's version
|
||||||
CFG_LLDB_VERSION=$($CFG_LLDB --version 2>/dev/null | head -1)
|
CFG_LLDB_VERSION=$($CFG_LLDB --version 2>/dev/null | head -1)
|
||||||
@ -802,7 +821,7 @@ step_msg "looking for target specific programs"
|
|||||||
|
|
||||||
probe CFG_ADB adb
|
probe CFG_ADB adb
|
||||||
|
|
||||||
if [ ! -z "$CFG_PANDOC" ]
|
if [ -n "$CFG_PANDOC" ]
|
||||||
then
|
then
|
||||||
# Extract "MAJOR MINOR" from Pandoc's version number
|
# Extract "MAJOR MINOR" from Pandoc's version number
|
||||||
PV_MAJOR_MINOR=$(pandoc --version | grep '^pandoc' |
|
PV_MAJOR_MINOR=$(pandoc --version | grep '^pandoc' |
|
||||||
@ -828,7 +847,7 @@ then
|
|||||||
BIN_SUF=.exe
|
BIN_SUF=.exe
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ ! -z "$CFG_ENABLE_LOCAL_RUST" ]
|
if [ -n "$CFG_ENABLE_LOCAL_RUST" ]
|
||||||
then
|
then
|
||||||
system_rustc=$(which rustc)
|
system_rustc=$(which rustc)
|
||||||
if [ -f ${CFG_LOCAL_RUST_ROOT}/bin/rustc${BIN_SUF} ]
|
if [ -f ${CFG_LOCAL_RUST_ROOT}/bin/rustc${BIN_SUF} ]
|
||||||
@ -899,20 +918,32 @@ then
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# If the clang isn't already enabled, check for GCC, and if it is missing, turn
|
||||||
|
# on clang as a backup.
|
||||||
|
if [ -z "$CFG_ENABLE_CLANG" ]
|
||||||
|
then
|
||||||
|
CFG_GCC_VERSION=$("$CFG_GCC" --version 2>&1)
|
||||||
|
if [ $? -ne 0 ]
|
||||||
|
then
|
||||||
|
step_msg "GCC not installed, will try using Clang"
|
||||||
|
CFG_ENABLE_CLANG=1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# Okay, at this point, we have made up our minds about whether we are
|
# Okay, at this point, we have made up our minds about whether we are
|
||||||
# going to force CFG_ENABLE_CLANG or not; save the setting if so.
|
# going to force CFG_ENABLE_CLANG or not; save the setting if so.
|
||||||
if [ ! -z "$CFG_ENABLE_CLANG" ]
|
if [ -n "$CFG_ENABLE_CLANG" ]
|
||||||
then
|
then
|
||||||
putvar CFG_ENABLE_CLANG
|
putvar CFG_ENABLE_CLANG
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Same with jemalloc. save the setting here.
|
# Same with jemalloc. save the setting here.
|
||||||
if [ ! -z "$CFG_DISABLE_JEMALLOC" ]
|
if [ -n "$CFG_DISABLE_JEMALLOC" ]
|
||||||
then
|
then
|
||||||
putvar CFG_DISABLE_JEMALLOC
|
putvar CFG_DISABLE_JEMALLOC
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ ! -z "$CFG_LLVM_ROOT" -a -z "$CFG_DISABLE_LLVM_VERSION_CHECK" -a -e "$CFG_LLVM_ROOT/bin/llvm-config" ]
|
if [ -n "$CFG_LLVM_ROOT" -a -z "$CFG_DISABLE_LLVM_VERSION_CHECK" -a -e "$CFG_LLVM_ROOT/bin/llvm-config" ]
|
||||||
then
|
then
|
||||||
step_msg "using custom LLVM at $CFG_LLVM_ROOT"
|
step_msg "using custom LLVM at $CFG_LLVM_ROOT"
|
||||||
|
|
||||||
@ -920,7 +951,7 @@ then
|
|||||||
LLVM_VERSION=$($LLVM_CONFIG --version)
|
LLVM_VERSION=$($LLVM_CONFIG --version)
|
||||||
|
|
||||||
case $LLVM_VERSION in
|
case $LLVM_VERSION in
|
||||||
(3.[5-6]*)
|
(3.[5-7]*)
|
||||||
msg "found ok version of LLVM: $LLVM_VERSION"
|
msg "found ok version of LLVM: $LLVM_VERSION"
|
||||||
;;
|
;;
|
||||||
(*)
|
(*)
|
||||||
@ -941,7 +972,7 @@ fi
|
|||||||
# CFG_ENABLE_CLANG is set, that indicates that we are opting into
|
# CFG_ENABLE_CLANG is set, that indicates that we are opting into
|
||||||
# running such safeguards.
|
# running such safeguards.
|
||||||
|
|
||||||
if [ ! -z "$CC" ]
|
if [ -n "$CC" ]
|
||||||
then
|
then
|
||||||
msg "skipping compiler inference steps; using provided CC=$CC"
|
msg "skipping compiler inference steps; using provided CC=$CC"
|
||||||
CFG_CC="$CC"
|
CFG_CC="$CC"
|
||||||
@ -954,7 +985,7 @@ then
|
|||||||
putvar CFG_USING_CLANG
|
putvar CFG_USING_CLANG
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
if [ ! -z "$CFG_ENABLE_CLANG" ]
|
if [ -n "$CFG_ENABLE_CLANG" ]
|
||||||
then
|
then
|
||||||
if [ -z "$CFG_CLANG" ]
|
if [ -z "$CFG_CLANG" ]
|
||||||
then
|
then
|
||||||
@ -968,23 +999,21 @@ else
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ ! -z "$CFG_ENABLE_CLANG" ]
|
if [ -n "$CFG_ENABLE_CLANG" ]
|
||||||
then
|
then
|
||||||
case "$CC" in
|
case "$CC" in
|
||||||
(''|*clang)
|
(''|*clang)
|
||||||
CFG_CLANG_REPORTED_VERSION=$($CFG_CC --version | grep version)
|
CFG_CLANG_REPORTED_VERSION=$($CFG_CC --version | grep version)
|
||||||
|
|
||||||
if [[ $CFG_CLANG_REPORTED_VERSION == *"(based on LLVM "* ]]
|
if echo $CFG_CLANG_REPORTED_VERSION | grep -q "(based on LLVM "; then
|
||||||
then
|
|
||||||
CFG_CLANG_VERSION=$(echo $CFG_CLANG_REPORTED_VERSION | sed 's/.*(based on LLVM \(.*\))/\1/')
|
CFG_CLANG_VERSION=$(echo $CFG_CLANG_REPORTED_VERSION | sed 's/.*(based on LLVM \(.*\))/\1/')
|
||||||
elif [[ $CFG_CLANG_REPORTED_VERSION == "Apple LLVM"* ]]
|
elif echo $CFG_CLANG_REPORTED_VERSION | grep -q "Apple LLVM"; then
|
||||||
then
|
|
||||||
CFG_OSX_CLANG_VERSION=$(echo $CFG_CLANG_REPORTED_VERSION | sed 's/.*version \(.*\) .*/\1/')
|
CFG_OSX_CLANG_VERSION=$(echo $CFG_CLANG_REPORTED_VERSION | sed 's/.*version \(.*\) .*/\1/')
|
||||||
else
|
else
|
||||||
CFG_CLANG_VERSION=$(echo $CFG_CLANG_REPORTED_VERSION | sed 's/.*version \(.*\) .*/\1/')
|
CFG_CLANG_VERSION=$(echo $CFG_CLANG_REPORTED_VERSION | sed 's/.*version \(.*\) .*/\1/')
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ ! -z "$CFG_OSX_CLANG_VERSION" ]
|
if [ -n "$CFG_OSX_CLANG_VERSION" ]
|
||||||
then
|
then
|
||||||
case $CFG_OSX_CLANG_VERSION in
|
case $CFG_OSX_CLANG_VERSION in
|
||||||
(7.0*)
|
(7.0*)
|
||||||
@ -1013,17 +1042,14 @@ then
|
|||||||
esac
|
esac
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ ! -z "$CFG_ENABLE_CCACHE" ]
|
if [ -n "$CFG_ENABLE_CCACHE" ]
|
||||||
then
|
then
|
||||||
if [ -z "$CC" ]
|
if [ -z "$CFG_CCACHE" ]
|
||||||
then
|
then
|
||||||
if [ -z "$CFG_CCACHE" ]
|
err "ccache requested but not found"
|
||||||
then
|
|
||||||
err "ccache requested but not found"
|
|
||||||
fi
|
|
||||||
|
|
||||||
CFG_CC="ccache $CFG_CC"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
CFG_CC="ccache $CFG_CC"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -z "$CC" -a -z "$CFG_ENABLE_CLANG" -a -z "$CFG_GCC" ]
|
if [ -z "$CC" -a -z "$CFG_ENABLE_CLANG" -a -z "$CFG_GCC" ]
|
||||||
@ -1052,6 +1078,14 @@ for target_file in ${CFG_SRC_DIR}mk/cfg/*.mk; do
|
|||||||
CFG_SUPPORTED_TARGET="${CFG_SUPPORTED_TARGET} $(basename "$target_file" .mk)"
|
CFG_SUPPORTED_TARGET="${CFG_SUPPORTED_TARGET} $(basename "$target_file" .mk)"
|
||||||
done
|
done
|
||||||
|
|
||||||
|
# copy build-triples to host-triples so that builds are a subset of hosts
|
||||||
|
V_TEMP=""
|
||||||
|
for i in $CFG_BUILD $CFG_HOST;
|
||||||
|
do
|
||||||
|
echo "$V_TEMP" | grep -qF $i || V_TEMP="$V_TEMP${V_TEMP:+ }$i"
|
||||||
|
done
|
||||||
|
CFG_HOST=$V_TEMP
|
||||||
|
|
||||||
# copy host-triples to target-triples so that hosts are a subset of targets
|
# copy host-triples to target-triples so that hosts are a subset of targets
|
||||||
V_TEMP=""
|
V_TEMP=""
|
||||||
for i in $CFG_HOST $CFG_TARGET;
|
for i in $CFG_HOST $CFG_TARGET;
|
||||||
@ -1078,20 +1112,24 @@ do
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
case $i in
|
case $i in
|
||||||
arm-linux-androideabi)
|
*android*)
|
||||||
|
upper_snake_target=$(echo "$i" | tr '[:lower:]' '[:upper:]' | tr '\-' '\_')
|
||||||
|
eval ndk=\$"CFG_${upper_snake_target}_NDK"
|
||||||
|
if [ -z "$ndk" ]
|
||||||
|
then
|
||||||
|
ndk=$CFG_ANDROID_CROSS_PATH
|
||||||
|
eval "CFG_${upper_snake_target}_NDK"=$CFG_ANDROID_CROSS_PATH
|
||||||
|
warn "generic/default Android NDK option is deprecated (use --$i-ndk option instead)"
|
||||||
|
fi
|
||||||
|
|
||||||
if [ ! -f $CFG_ANDROID_CROSS_PATH/bin/arm-linux-androideabi-gcc ]
|
# Perform a basic sanity check of the NDK
|
||||||
then
|
for android_ndk_tool in "$ndk/bin/$i-gcc" "$ndk/bin/$i-g++" "$ndk/bin/$i-ar"
|
||||||
err "NDK $CFG_ANDROID_CROSS_PATH/bin/arm-linux-androideabi-gcc not found"
|
do
|
||||||
fi
|
if [ ! -f $android_ndk_tool ]
|
||||||
if [ ! -f $CFG_ANDROID_CROSS_PATH/bin/arm-linux-androideabi-g++ ]
|
then
|
||||||
then
|
err "NDK tool $android_ndk_tool not found (bad or missing --$i-ndk option?)"
|
||||||
err "NDK $CFG_ANDROID_CROSS_PATH/bin/arm-linux-androideabi-g++ not found"
|
fi
|
||||||
fi
|
done
|
||||||
if [ ! -f $CFG_ANDROID_CROSS_PATH/bin/arm-linux-androideabi-ar ]
|
|
||||||
then
|
|
||||||
err "NDK $CFG_ANDROID_CROSS_PATH/bin/arm-linux-androideabi-ar not found"
|
|
||||||
fi
|
|
||||||
;;
|
;;
|
||||||
|
|
||||||
arm-apple-darwin)
|
arm-apple-darwin)
|
||||||
@ -1109,7 +1147,7 @@ do
|
|||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
|
|
||||||
x86_64-*-msvc)
|
*-msvc)
|
||||||
# Currently the build system is not configured to build jemalloc
|
# Currently the build system is not configured to build jemalloc
|
||||||
# with MSVC, so we omit this optional dependency.
|
# with MSVC, so we omit this optional dependency.
|
||||||
step_msg "targeting MSVC, disabling jemalloc"
|
step_msg "targeting MSVC, disabling jemalloc"
|
||||||
@ -1143,28 +1181,56 @@ do
|
|||||||
# INCLUDE and LIB variables for MSVC so we can set those in the
|
# INCLUDE and LIB variables for MSVC so we can set those in the
|
||||||
# build system as well.
|
# build system as well.
|
||||||
install=$(reg QUERY \
|
install=$(reg QUERY \
|
||||||
'HKLM\SOFTWARE\Wow6432Node\Microsoft\VisualStudio\12.0' \
|
'HKLM\SOFTWARE\Wow6432Node\Microsoft\VisualStudio\14.0' \
|
||||||
-v InstallDir)
|
-v InstallDir)
|
||||||
|
if [ -z "$install" ]; then
|
||||||
|
install=$(reg QUERY \
|
||||||
|
'HKLM\SOFTWARE\Wow6432Node\Microsoft\VisualStudio\12.0' \
|
||||||
|
-v InstallDir)
|
||||||
|
fi
|
||||||
need_ok "couldn't find visual studio install root"
|
need_ok "couldn't find visual studio install root"
|
||||||
CFG_MSVC_ROOT=$(echo "$install" | grep InstallDir | sed 's/.*REG_SZ[ ]*//')
|
CFG_MSVC_ROOT=$(echo "$install" | grep InstallDir | sed 's/.*REG_SZ[ ]*//')
|
||||||
CFG_MSVC_ROOT=$(dirname "$CFG_MSVC_ROOT")
|
CFG_MSVC_ROOT=$(dirname "$CFG_MSVC_ROOT")
|
||||||
CFG_MSVC_ROOT=$(dirname "$CFG_MSVC_ROOT")
|
CFG_MSVC_ROOT=$(dirname "$CFG_MSVC_ROOT")
|
||||||
CFG_MSVC_CL="${CFG_MSVC_ROOT}/VC/bin/amd64/cl.exe"
|
putvar CFG_MSVC_ROOT
|
||||||
CFG_MSVC_LIB="${CFG_MSVC_ROOT}/VC/bin/amd64/lib.exe"
|
|
||||||
CFG_MSVC_LINK="${CFG_MSVC_ROOT}/VC/bin/amd64/link.exe"
|
case $i in
|
||||||
|
x86_64-*)
|
||||||
|
bits=x86_64
|
||||||
|
msvc_part=amd64
|
||||||
|
;;
|
||||||
|
i686-*)
|
||||||
|
bits=i386
|
||||||
|
msvc_part=
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
err "can only target x86 targets for MSVC"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
bindir="${CFG_MSVC_ROOT}/VC/bin"
|
||||||
|
if [ -n "$msvc_part" ]; then
|
||||||
|
bindir="$bindir/$msvc_part"
|
||||||
|
fi
|
||||||
|
eval CFG_MSVC_BINDIR_$bits="\"$bindir\""
|
||||||
|
eval CFG_MSVC_CL_$bits="\"$bindir/cl.exe\""
|
||||||
|
eval CFG_MSVC_LIB_$bits="\"$bindir/lib.exe\""
|
||||||
|
eval CFG_MSVC_LINK_$bits="\"$bindir/link.exe\""
|
||||||
|
|
||||||
vcvarsall="${CFG_MSVC_ROOT}/VC/vcvarsall.bat"
|
vcvarsall="${CFG_MSVC_ROOT}/VC/vcvarsall.bat"
|
||||||
CFG_MSVC_INCLUDE_PATH=$(cmd /c "\"$vcvarsall\" amd64 && cmd /c echo %INCLUDE%")
|
include_path=$(cmd /c "\"$vcvarsall\" $msvc_part && cmd /c echo %INCLUDE%")
|
||||||
need_ok "failed to learn about MSVC's INCLUDE"
|
need_ok "failed to learn about MSVC's INCLUDE"
|
||||||
CFG_MSVC_LIB_PATH=$(cmd /c "\"$vcvarsall\" amd64 && cmd /c echo %LIB%")
|
lib_path=$(cmd /c "\"$vcvarsall\" $msvc_part && cmd /c echo %LIB%")
|
||||||
need_ok "failed to learn about MSVC's LIB"
|
need_ok "failed to learn about MSVC's LIB"
|
||||||
|
|
||||||
putvar CFG_MSVC_ROOT
|
eval CFG_MSVC_INCLUDE_PATH_${bits}="\"$include_path\""
|
||||||
putvar CFG_MSVC_CL
|
eval CFG_MSVC_LIB_PATH_${bits}="\"$lib_path\""
|
||||||
putvar CFG_MSVC_LIB
|
|
||||||
putvar CFG_MSVC_LINK
|
putvar CFG_MSVC_BINDIR_${bits}
|
||||||
putvar CFG_MSVC_INCLUDE_PATH
|
putvar CFG_MSVC_CL_${bits}
|
||||||
putvar CFG_MSVC_LIB_PATH
|
putvar CFG_MSVC_LIB_${bits}
|
||||||
|
putvar CFG_MSVC_LINK_${bits}
|
||||||
|
putvar CFG_MSVC_INCLUDE_PATH_${bits}
|
||||||
|
putvar CFG_MSVC_LIB_PATH_${bits}
|
||||||
;;
|
;;
|
||||||
|
|
||||||
*)
|
*)
|
||||||
@ -1172,7 +1238,7 @@ do
|
|||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
if [ ! -z "$CFG_PERF" ]
|
if [ -n "$CFG_PERF" ]
|
||||||
then
|
then
|
||||||
HAVE_PERF_LOGFD=`$CFG_PERF stat --log-fd 2>&1 | grep 'unknown option'`
|
HAVE_PERF_LOGFD=`$CFG_PERF stat --log-fd 2>&1 | grep 'unknown option'`
|
||||||
if [ -z "$HAVE_PERF_LOGFD" ];
|
if [ -z "$HAVE_PERF_LOGFD" ];
|
||||||
@ -1282,11 +1348,11 @@ then
|
|||||||
"${CFG_GIT}" submodule init
|
"${CFG_GIT}" submodule init
|
||||||
|
|
||||||
# Disable submodules that we're not using
|
# Disable submodules that we're not using
|
||||||
if [ ! -z "${CFG_LLVM_ROOT}" ]; then
|
if [ -n "${CFG_LLVM_ROOT}" ]; then
|
||||||
msg "git: submodule deinit src/llvm"
|
msg "git: submodule deinit src/llvm"
|
||||||
"${CFG_GIT}" submodule deinit src/llvm
|
"${CFG_GIT}" submodule deinit src/llvm
|
||||||
fi
|
fi
|
||||||
if [ ! -z "${CFG_JEMALLOC_ROOT}" ]; then
|
if [ -n "${CFG_JEMALLOC_ROOT}" ]; then
|
||||||
msg "git: submodule deinit src/jemalloc"
|
msg "git: submodule deinit src/jemalloc"
|
||||||
"${CFG_GIT}" submodule deinit src/jemalloc
|
"${CFG_GIT}" submodule deinit src/jemalloc
|
||||||
fi
|
fi
|
||||||
@ -1333,7 +1399,7 @@ do
|
|||||||
if [ -z $CFG_LLVM_ROOT ]
|
if [ -z $CFG_LLVM_ROOT ]
|
||||||
then
|
then
|
||||||
LLVM_BUILD_DIR=${CFG_BUILD_DIR}$t/llvm
|
LLVM_BUILD_DIR=${CFG_BUILD_DIR}$t/llvm
|
||||||
if [ ! -z "$CFG_DISABLE_OPTIMIZE_LLVM" ]
|
if [ -n "$CFG_DISABLE_OPTIMIZE_LLVM" ]
|
||||||
then
|
then
|
||||||
LLVM_DBG_OPTS="--enable-debug-symbols --disable-optimized"
|
LLVM_DBG_OPTS="--enable-debug-symbols --disable-optimized"
|
||||||
# Just use LLVM straight from its build directory to
|
# Just use LLVM straight from its build directory to
|
||||||
@ -1389,7 +1455,7 @@ do
|
|||||||
msg "configuring LLVM for $t with cmake"
|
msg "configuring LLVM for $t with cmake"
|
||||||
|
|
||||||
CMAKE_ARGS="-DLLVM_INCLUDE_TESTS=OFF"
|
CMAKE_ARGS="-DLLVM_INCLUDE_TESTS=OFF"
|
||||||
if [ ! -z "$CFG_DISABLE_OPTIMIZE_LLVM" ]; then
|
if [ -n "$CFG_DISABLE_OPTIMIZE_LLVM" ]; then
|
||||||
CMAKE_ARGS="$CMAKE_ARGS -DCMAKE_BUILD_TYPE=Debug"
|
CMAKE_ARGS="$CMAKE_ARGS -DCMAKE_BUILD_TYPE=Debug"
|
||||||
else
|
else
|
||||||
CMAKE_ARGS="$CMAKE_ARGS -DCMAKE_BUILD_TYPE=Release"
|
CMAKE_ARGS="$CMAKE_ARGS -DCMAKE_BUILD_TYPE=Release"
|
||||||
@ -1403,8 +1469,29 @@ do
|
|||||||
|
|
||||||
msg "configuring LLVM with:"
|
msg "configuring LLVM with:"
|
||||||
msg "$CMAKE_ARGS"
|
msg "$CMAKE_ARGS"
|
||||||
|
case "$CFG_MSVC_ROOT" in
|
||||||
|
*14.0*)
|
||||||
|
generator="Visual Studio 14 2015"
|
||||||
|
;;
|
||||||
|
*12.0*)
|
||||||
|
generator="Visual Studio 12 2013"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
err "can't determine generator for LLVM cmake"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
case "$t" in
|
||||||
|
x86_64-*)
|
||||||
|
generator="$generator Win64"
|
||||||
|
;;
|
||||||
|
i686-*)
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
err "can only build LLVM for x86 platforms"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
(cd $LLVM_BUILD_DIR && "$CFG_CMAKE" $CFG_LLVM_SRC_DIR \
|
(cd $LLVM_BUILD_DIR && "$CFG_CMAKE" $CFG_LLVM_SRC_DIR \
|
||||||
-G "Visual Studio 12 2013 Win64" \
|
-G "$generator" \
|
||||||
$CMAKE_ARGS)
|
$CMAKE_ARGS)
|
||||||
need_ok "LLVM cmake configure failed"
|
need_ok "LLVM cmake configure failed"
|
||||||
fi
|
fi
|
||||||
@ -1470,11 +1557,26 @@ do
|
|||||||
|
|
||||||
(*)
|
(*)
|
||||||
msg "inferring LLVM_CXX/CC from CXX/CC = $CXX/$CC"
|
msg "inferring LLVM_CXX/CC from CXX/CC = $CXX/$CC"
|
||||||
LLVM_CXX_32="$CXX"
|
if [ -n "$CFG_ENABLE_CCACHE" ]
|
||||||
LLVM_CC_32="$CC"
|
then
|
||||||
|
if [ -z "$CFG_CCACHE" ]
|
||||||
|
then
|
||||||
|
err "ccache requested but not found"
|
||||||
|
fi
|
||||||
|
|
||||||
|
LLVM_CXX_32="ccache $CXX"
|
||||||
|
LLVM_CC_32="ccache $CC"
|
||||||
|
|
||||||
|
LLVM_CXX_64="ccache $CXX"
|
||||||
|
LLVM_CC_64="ccache $CC"
|
||||||
|
else
|
||||||
|
LLVM_CXX_32="$CXX"
|
||||||
|
LLVM_CC_32="$CC"
|
||||||
|
|
||||||
|
LLVM_CXX_64="$CXX"
|
||||||
|
LLVM_CC_64="$CC"
|
||||||
|
fi
|
||||||
|
|
||||||
LLVM_CXX_64="$CXX"
|
|
||||||
LLVM_CC_64="$CC"
|
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
@ -1584,25 +1686,26 @@ putvar CFG_HOST
|
|||||||
putvar CFG_TARGET
|
putvar CFG_TARGET
|
||||||
putvar CFG_LIBDIR_RELATIVE
|
putvar CFG_LIBDIR_RELATIVE
|
||||||
putvar CFG_DISABLE_MANAGE_SUBMODULES
|
putvar CFG_DISABLE_MANAGE_SUBMODULES
|
||||||
putvar CFG_ANDROID_CROSS_PATH
|
putvar CFG_AARCH64_LINUX_ANDROID_NDK
|
||||||
|
putvar CFG_ARM_LINUX_ANDROIDEABI_NDK
|
||||||
putvar CFG_MANDIR
|
putvar CFG_MANDIR
|
||||||
|
|
||||||
# Avoid spurious warnings from clang by feeding it original source on
|
# Avoid spurious warnings from clang by feeding it original source on
|
||||||
# ccache-miss rather than preprocessed input.
|
# ccache-miss rather than preprocessed input.
|
||||||
if [ ! -z "$CFG_ENABLE_CCACHE" ] && [ ! -z "$CFG_USING_CLANG" ]
|
if [ -n "$CFG_ENABLE_CCACHE" ] && [ -n "$CFG_USING_CLANG" ]
|
||||||
then
|
then
|
||||||
CFG_CCACHE_CPP2=1
|
CFG_CCACHE_CPP2=1
|
||||||
putvar CFG_CCACHE_CPP2
|
putvar CFG_CCACHE_CPP2
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ ! -z "$CFG_ENABLE_CCACHE" ]
|
if [ -n "$CFG_ENABLE_CCACHE" ]
|
||||||
then
|
then
|
||||||
CFG_CCACHE_BASEDIR=${CFG_SRC_DIR}
|
CFG_CCACHE_BASEDIR=${CFG_SRC_DIR}
|
||||||
putvar CFG_CCACHE_BASEDIR
|
putvar CFG_CCACHE_BASEDIR
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
if [ ! -z $BAD_PANDOC ]
|
if [ -n $BAD_PANDOC ]
|
||||||
then
|
then
|
||||||
CFG_PANDOC=
|
CFG_PANDOC=
|
||||||
putvar CFG_PANDOC
|
putvar CFG_PANDOC
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
# aarch64-linux-android configuration
|
# aarch64-linux-android configuration
|
||||||
# CROSS_PREFIX_aarch64-linux-android-
|
# CROSS_PREFIX_aarch64-linux-android-
|
||||||
CC_aarch64-linux-android=$(CFG_ANDROID_CROSS_PATH)/bin/aarch64-linux-android-gcc
|
CC_aarch64-linux-android=$(CFG_AARCH64_LINUX_ANDROID_NDK)/bin/aarch64-linux-android-gcc
|
||||||
CXX_aarch64-linux-android=$(CFG_ANDROID_CROSS_PATH)/bin/aarch64-linux-android-g++
|
CXX_aarch64-linux-android=$(CFG_AARCH64_LINUX_ANDROID_NDK)/bin/aarch64-linux-android-g++
|
||||||
CPP_aarch64-linux-android=$(CFG_ANDROID_CROSS_PATH)/bin/aarch64-linux-android-gcc -E
|
CPP_aarch64-linux-android=$(CFG_AARCH64_LINUX_ANDROID_NDK)/bin/aarch64-linux-android-gcc -E
|
||||||
AR_aarch64-linux-android=$(CFG_ANDROID_CROSS_PATH)/bin/aarch64-linux-android-ar
|
AR_aarch64-linux-android=$(CFG_AARCH64_LINUX_ANDROID_NDK)/bin/aarch64-linux-android-ar
|
||||||
CFG_LIB_NAME_aarch64-linux-android=lib$(1).so
|
CFG_LIB_NAME_aarch64-linux-android=lib$(1).so
|
||||||
CFG_STATIC_LIB_NAME_aarch64-linux-android=lib$(1).a
|
CFG_STATIC_LIB_NAME_aarch64-linux-android=lib$(1).a
|
||||||
CFG_LIB_GLOB_aarch64-linux-android=lib$(1)-*.so
|
CFG_LIB_GLOB_aarch64-linux-android=lib$(1)-*.so
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
# arm-linux-androideabi configuration
|
# arm-linux-androideabi configuration
|
||||||
CC_arm-linux-androideabi=$(CFG_ANDROID_CROSS_PATH)/bin/arm-linux-androideabi-gcc
|
CC_arm-linux-androideabi=$(CFG_ARM_LINUX_ANDROIDEABI_NDK)/bin/arm-linux-androideabi-gcc
|
||||||
CXX_arm-linux-androideabi=$(CFG_ANDROID_CROSS_PATH)/bin/arm-linux-androideabi-g++
|
CXX_arm-linux-androideabi=$(CFG_ARM_LINUX_ANDROIDEABI_NDK)/bin/arm-linux-androideabi-g++
|
||||||
CPP_arm-linux-androideabi=$(CFG_ANDROID_CROSS_PATH)/bin/arm-linux-androideabi-gcc -E
|
CPP_arm-linux-androideabi=$(CFG_ARM_LINUX_ANDROIDEABI_NDK)/bin/arm-linux-androideabi-gcc -E
|
||||||
AR_arm-linux-androideabi=$(CFG_ANDROID_CROSS_PATH)/bin/arm-linux-androideabi-ar
|
AR_arm-linux-androideabi=$(CFG_ARM_LINUX_ANDROIDEABI_NDK)/bin/arm-linux-androideabi-ar
|
||||||
CFG_LIB_NAME_arm-linux-androideabi=lib$(1).so
|
CFG_LIB_NAME_arm-linux-androideabi=lib$(1).so
|
||||||
CFG_STATIC_LIB_NAME_arm-linux-androideabi=lib$(1).a
|
CFG_STATIC_LIB_NAME_arm-linux-androideabi=lib$(1).a
|
||||||
CFG_LIB_GLOB_arm-linux-androideabi=lib$(1)-*.so
|
CFG_LIB_GLOB_arm-linux-androideabi=lib$(1)-*.so
|
||||||
|
29
mk/cfg/i686-pc-windows-msvc.mk
Normal file
29
mk/cfg/i686-pc-windows-msvc.mk
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
# i686-pc-windows-msvc configuration
|
||||||
|
CC_i686-pc-windows-msvc="$(CFG_MSVC_CL_i386)" -nologo
|
||||||
|
LINK_i686-pc-windows-msvc="$(CFG_MSVC_LINK_i386)" -nologo
|
||||||
|
CXX_i686-pc-windows-msvc="$(CFG_MSVC_CL_i386)" -nologo
|
||||||
|
CPP_i686-pc-windows-msvc="$(CFG_MSVC_CL_i386)" -nologo
|
||||||
|
AR_i686-pc-windows-msvc="$(CFG_MSVC_LIB_i386)" -nologo
|
||||||
|
CFG_LIB_NAME_i686-pc-windows-msvc=$(1).dll
|
||||||
|
CFG_STATIC_LIB_NAME_i686-pc-windows-msvc=$(1).lib
|
||||||
|
CFG_LIB_GLOB_i686-pc-windows-msvc=$(1)-*.{dll,lib}
|
||||||
|
CFG_LIB_DSYM_GLOB_i686-pc-windows-msvc=$(1)-*.dylib.dSYM
|
||||||
|
CFG_JEMALLOC_CFLAGS_i686-pc-windows-msvc :=
|
||||||
|
CFG_GCCISH_CFLAGS_i686-pc-windows-msvc := -MD
|
||||||
|
CFG_GCCISH_CXXFLAGS_i686-pc-windows-msvc := -MD
|
||||||
|
CFG_GCCISH_LINK_FLAGS_i686-pc-windows-msvc :=
|
||||||
|
CFG_GCCISH_DEF_FLAG_i686-pc-windows-msvc :=
|
||||||
|
CFG_LLC_FLAGS_i686-pc-windows-msvc :=
|
||||||
|
CFG_INSTALL_NAME_i686-pc-windows-msvc =
|
||||||
|
CFG_EXE_SUFFIX_i686-pc-windows-msvc := .exe
|
||||||
|
CFG_WINDOWSY_i686-pc-windows-msvc := 1
|
||||||
|
CFG_UNIXY_i686-pc-windows-msvc :=
|
||||||
|
CFG_LDPATH_i686-pc-windows-msvc :=
|
||||||
|
CFG_RUN_i686-pc-windows-msvc=$(2)
|
||||||
|
CFG_RUN_TARG_i686-pc-windows-msvc=$(call CFG_RUN_i686-pc-windows-msvc,,$(2))
|
||||||
|
CFG_GNU_TRIPLE_i686-pc-windows-msvc := i686-pc-win32
|
||||||
|
|
||||||
|
# All windows nightiles are currently a GNU triple, so this MSVC triple is not
|
||||||
|
# bootstrapping from itself. This is relevant during stage0, and other parts of
|
||||||
|
# the build system take this into account.
|
||||||
|
BOOTSTRAP_FROM_i686-pc-windows-msvc := i686-pc-windows-gnu
|
22
mk/cfg/i686-unknown-freebsd.mk
Normal file
22
mk/cfg/i686-unknown-freebsd.mk
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
# i686-unknown-freebsd configuration
|
||||||
|
CC_i686-unknown-freebsd=$(CC)
|
||||||
|
CXX_i686-unknown-freebsd=$(CXX)
|
||||||
|
CPP_i686-unknown-freebsd=$(CPP)
|
||||||
|
AR_i686-unknown-freebsd=$(AR)
|
||||||
|
CFG_LIB_NAME_i686-unknown-freebsd=lib$(1).so
|
||||||
|
CFG_STATIC_LIB_NAME_i686-unknown-freebsd=lib$(1).a
|
||||||
|
CFG_LIB_GLOB_i686-unknown-freebsd=lib$(1)-*.so
|
||||||
|
CFG_LIB_DSYM_GLOB_i686-unknown-freebsd=$(1)-*.dylib.dSYM
|
||||||
|
CFG_JEMALLOC_CFLAGS_i686-unknown-freebsd := -m32 -arch i386 -I/usr/local/include $(CFLAGS)
|
||||||
|
CFG_GCCISH_CFLAGS_i686-unknown-freebsd := -Wall -Werror -g -fPIC -m32 -arch i386 -I/usr/local/include $(CFLAGS)
|
||||||
|
CFG_GCCISH_LINK_FLAGS_i686-unknown-freebsd := -m32 -shared -fPIC -g -pthread -lrt
|
||||||
|
CFG_GCCISH_DEF_FLAG_i686-unknown-freebsd := -Wl,--export-dynamic,--dynamic-list=
|
||||||
|
CFG_LLC_FLAGS_i686-unknown-freebsd :=
|
||||||
|
CFG_INSTALL_NAME_i686-unknown-freebsd =
|
||||||
|
CFG_EXE_SUFFIX_i686-unknown-freebsd :=
|
||||||
|
CFG_WINDOWSY_i686-unknown-freebsd :=
|
||||||
|
CFG_UNIXY_i686-unknown-freebsd := 1
|
||||||
|
CFG_LDPATH_i686-unknown-freebsd :=
|
||||||
|
CFG_RUN_i686-unknown-freebsd=$(2)
|
||||||
|
CFG_RUN_TARG_i686-unknown-freebsd=$(call CFG_RUN_i686-unknown-freebsd,,$(2))
|
||||||
|
CFG_GNU_TRIPLE_i686-unknown-freebsd := i686-unknown-freebsd
|
@ -1,9 +1,9 @@
|
|||||||
# x86_64-pc-windows-msvc configuration
|
# x86_64-pc-windows-msvc configuration
|
||||||
CC_x86_64-pc-windows-msvc="$(CFG_MSVC_CL)" -nologo
|
CC_x86_64-pc-windows-msvc="$(CFG_MSVC_CL_x86_64)" -nologo
|
||||||
LINK_x86_64-pc-windows-msvc="$(CFG_MSVC_LINK)" -nologo
|
LINK_x86_64-pc-windows-msvc="$(CFG_MSVC_LINK_x86_64)" -nologo
|
||||||
CXX_x86_64-pc-windows-msvc="$(CFG_MSVC_CL)" -nologo
|
CXX_x86_64-pc-windows-msvc="$(CFG_MSVC_CL_x86_64)" -nologo
|
||||||
CPP_x86_64-pc-windows-msvc="$(CFG_MSVC_CL)" -nologo
|
CPP_x86_64-pc-windows-msvc="$(CFG_MSVC_CL_x86_64)" -nologo
|
||||||
AR_x86_64-pc-windows-msvc="$(CFG_MSVC_LIB)" -nologo
|
AR_x86_64-pc-windows-msvc="$(CFG_MSVC_LIB_x86_64)" -nologo
|
||||||
CFG_LIB_NAME_x86_64-pc-windows-msvc=$(1).dll
|
CFG_LIB_NAME_x86_64-pc-windows-msvc=$(1).dll
|
||||||
CFG_STATIC_LIB_NAME_x86_64-pc-windows-msvc=$(1).lib
|
CFG_STATIC_LIB_NAME_x86_64-pc-windows-msvc=$(1).lib
|
||||||
CFG_LIB_GLOB_x86_64-pc-windows-msvc=$(1)-*.{dll,lib}
|
CFG_LIB_GLOB_x86_64-pc-windows-msvc=$(1)-*.{dll,lib}
|
||||||
@ -23,64 +23,6 @@ CFG_RUN_x86_64-pc-windows-msvc=$(2)
|
|||||||
CFG_RUN_TARG_x86_64-pc-windows-msvc=$(call CFG_RUN_x86_64-pc-windows-msvc,,$(2))
|
CFG_RUN_TARG_x86_64-pc-windows-msvc=$(call CFG_RUN_x86_64-pc-windows-msvc,,$(2))
|
||||||
CFG_GNU_TRIPLE_x86_64-pc-windows-msvc := x86_64-pc-win32
|
CFG_GNU_TRIPLE_x86_64-pc-windows-msvc := x86_64-pc-win32
|
||||||
|
|
||||||
# These two environment variables are scraped by the `./configure` script and
|
|
||||||
# are necessary for `cl.exe` to find standard headers (the INCLUDE variable) and
|
|
||||||
# for `link.exe` to find standard libraries (the LIB variable).
|
|
||||||
ifdef CFG_MSVC_INCLUDE_PATH
|
|
||||||
export INCLUDE := $(CFG_MSVC_INCLUDE_PATH)
|
|
||||||
endif
|
|
||||||
ifdef CFG_MSVC_LIB_PATH
|
|
||||||
export LIB := $(CFG_MSVC_LIB_PATH)
|
|
||||||
endif
|
|
||||||
|
|
||||||
# Unfortunately `link.exe` is also a program in `/usr/bin` on MinGW installs,
|
|
||||||
# but it's not the one that we want. As a result we make sure that our detected
|
|
||||||
# `link.exe` shows up in PATH first.
|
|
||||||
ifdef CFG_MSVC_LINK
|
|
||||||
export PATH := $(CFG_MSVC_ROOT)/VC/bin/amd64:$(PATH)
|
|
||||||
endif
|
|
||||||
|
|
||||||
# There are more comments about this available in the target specification for
|
|
||||||
# Windows MSVC in the compiler, but the gist of it is that we use `llvm-ar.exe`
|
|
||||||
# instead of `lib.exe` for assembling archives, so we need to inject this custom
|
|
||||||
# dependency here.
|
|
||||||
NATIVE_TOOL_DEPS_core_T_x86_64-pc-windows-msvc += llvm-ar.exe
|
|
||||||
INSTALLED_BINS_x86_64-pc-windows-msvc += llvm-ar.exe
|
|
||||||
|
|
||||||
# When working with MSVC on windows, each DLL needs to explicitly declare its
|
|
||||||
# interface to the outside world through some means. The options for doing so
|
|
||||||
# include:
|
|
||||||
#
|
|
||||||
# 1. A custom attribute on each function itself
|
|
||||||
# 2. A linker argument saying what to export
|
|
||||||
# 3. A file which lists all symbols that need to be exported
|
|
||||||
#
|
|
||||||
# The Rust compiler takes care (1) for us for all Rust code by annotating all
|
|
||||||
# public-facing functions with dllexport, but we have a few native dependencies
|
|
||||||
# which need to cross the DLL boundary. The most important of these dependencies
|
|
||||||
# is LLVM which is linked into `rustc_llvm.dll` but primarily used from
|
|
||||||
# `rustc_trans.dll`. This means that many of LLVM's C API functions need to be
|
|
||||||
# exposed from `rustc_llvm.dll` to be forwarded over the boundary.
|
|
||||||
#
|
|
||||||
# Unfortunately, at this time, LLVM does not handle this sort of exportation on
|
|
||||||
# Windows for us, so we're forced to do it ourselves if we want it (which seems
|
|
||||||
# like the path of least resistance right now). To do this we generate a `.DEF`
|
|
||||||
# file [1] which we then custom-pass to the linker when building the rustc_llvm
|
|
||||||
# crate. This DEF file list all symbols that are exported from
|
|
||||||
# `src/librustc_llvm/lib.rs` and is generated by a small python script.
|
|
||||||
#
|
|
||||||
# Fun times!
|
|
||||||
#
|
|
||||||
# [1]: https://msdn.microsoft.com/en-us/library/28d6s79h.aspx
|
|
||||||
RUSTFLAGS_rustc_llvm_T_x86_64-pc-windows-msvc += \
|
|
||||||
-C link-args="-DEF:x86_64-pc-windows-msvc/rt/rustc_llvm.def"
|
|
||||||
CUSTOM_DEPS_rustc_llvm_T_x86_64-pc-windows-msvc += \
|
|
||||||
x86_64-pc-windows-msvc/rt/rustc_llvm.def
|
|
||||||
|
|
||||||
x86_64-pc-windows-msvc/rt/rustc_llvm.def: $(S)src/etc/mklldef.py \
|
|
||||||
$(S)src/librustc_llvm/lib.rs
|
|
||||||
$(CFG_PYTHON) $^ $@ rustc_llvm-$(CFG_FILENAME_EXTRA)
|
|
||||||
|
|
||||||
# All windows nightiles are currently a GNU triple, so this MSVC triple is not
|
# All windows nightiles are currently a GNU triple, so this MSVC triple is not
|
||||||
# bootstrapping from itself. This is relevant during stage0, and other parts of
|
# bootstrapping from itself. This is relevant during stage0, and other parts of
|
||||||
# the build system take this into account.
|
# the build system take this into account.
|
||||||
|
22
mk/cfg/x86_64-unknown-netbsd.mk
Normal file
22
mk/cfg/x86_64-unknown-netbsd.mk
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
# x86_64-unknown-netbsd configuration
|
||||||
|
CC_x86_64-unknown-netbsd=$(CC)
|
||||||
|
CXX_x86_64-unknown-netbsd=$(CXX)
|
||||||
|
CPP_x86_64-unknown-netbsd=$(CPP)
|
||||||
|
AR_x86_64-unknown-netbsd=$(AR)
|
||||||
|
CFG_LIB_NAME_x86_64-unknown-netbsd=lib$(1).so
|
||||||
|
CFG_STATIC_LIB_NAME_x86_64-unknown-netbsd=lib$(1).a
|
||||||
|
CFG_LIB_GLOB_x86_64-unknown-netbsd=lib$(1)-*.so
|
||||||
|
CFG_LIB_DSYM_GLOB_x86_64-unknown-netbsd=$(1)-*.dylib.dSYM
|
||||||
|
CFG_JEMALLOC_CFLAGS_x86_64-unknown-netbsd := -I/usr/local/include $(CFLAGS)
|
||||||
|
CFG_GCCISH_CFLAGS_x86_64-unknown-netbsd := -Wall -Werror -g -fPIC -I/usr/local/include $(CFLAGS)
|
||||||
|
CFG_GCCISH_LINK_FLAGS_x86_64-unknown-netbsd := -shared -fPIC -g -pthread -lrt
|
||||||
|
CFG_GCCISH_DEF_FLAG_x86_64-unknown-netbsd := -Wl,--export-dynamic,--dynamic-list=
|
||||||
|
CFG_LLC_FLAGS_x86_64-unknown-netbsd :=
|
||||||
|
CFG_INSTALL_NAME_x86_64-unknown-netbsd =
|
||||||
|
CFG_EXE_SUFFIX_x86_64-unknown-netbsd :=
|
||||||
|
CFG_WINDOWSY_x86_64-unknown-netbsd :=
|
||||||
|
CFG_UNIXY_x86_64-unknown-netbsd := 1
|
||||||
|
CFG_LDPATH_x86_64-unknown-netbsd :=
|
||||||
|
CFG_RUN_x86_64-unknown-netbsd=$(2)
|
||||||
|
CFG_RUN_TARG_x86_64-unknown-netbsd=$(call CFG_RUN_x86_64-unknown-netbsd,,$(2))
|
||||||
|
CFG_GNU_TRIPLE_x86_64-unknown-netbsd := x86_64-unknown-netbsd
|
23
mk/ctags.mk
23
mk/ctags.mk
@ -15,24 +15,11 @@
|
|||||||
|
|
||||||
.PHONY: TAGS.emacs TAGS.vi
|
.PHONY: TAGS.emacs TAGS.vi
|
||||||
|
|
||||||
# This is using a blacklist approach, probably more durable than a whitelist.
|
CTAGS_LOCATIONS=$(wildcard ${CFG_SRC_DIR}src/lib*)
|
||||||
# We exclude: external dependencies (llvm, rt/{msvc,vg}),
|
CTAGS_LOCATIONS=$(patsubst ${CFG_SRC_DIR}src/librust%,, \
|
||||||
# tests (compiletest, test) and a couple of other things (rt/arch, etc)
|
$(patsubst ${CFG_SRC_DIR}src/lib%test,, \
|
||||||
CTAGS_LOCATIONS=$(patsubst ${CFG_SRC_DIR}src/llvm,, \
|
$(wildcard ${CFG_SRC_DIR}src/lib*))) ${CFG_SRC_DIR}src/libtest
|
||||||
$(patsubst ${CFG_SRC_DIR}src/compiletest,, \
|
CTAGS_OPTS=--options="${CFG_SRC_DIR}src/etc/ctags.rust" --languages=Rust --recurse ${CTAGS_LOCATIONS}
|
||||||
$(patsubst ${CFG_SRC_DIR}src/test,, \
|
|
||||||
$(patsubst ${CFG_SRC_DIR}src/etc,, \
|
|
||||||
$(patsubst ${CFG_SRC_DIR}src/rt,, \
|
|
||||||
$(patsubst ${CFG_SRC_DIR}src/rt/arch,, \
|
|
||||||
$(patsubst ${CFG_SRC_DIR}src/rt/msvc,, \
|
|
||||||
$(patsubst ${CFG_SRC_DIR}src/rt/vg,, \
|
|
||||||
$(wildcard ${CFG_SRC_DIR}src/*) $(wildcard ${CFG_SRC_DIR}src/rt/*) \
|
|
||||||
))))))))
|
|
||||||
CTAGS_OPTS=--options="${CFG_SRC_DIR}src/etc/ctags.rust" --languages=-javascript --recurse ${CTAGS_LOCATIONS}
|
|
||||||
# We could use `--languages=Rust`, but there is value in producing tags for the
|
|
||||||
# C++ parts of the code base too (at the time of writing, those are .h and .cpp
|
|
||||||
# files in src/rt, src/rt/sync and src/rustllvm); we mainly just want to
|
|
||||||
# exclude the external dependencies.
|
|
||||||
|
|
||||||
TAGS.emacs:
|
TAGS.emacs:
|
||||||
ctags -e -f $@ ${CTAGS_OPTS}
|
ctags -e -f $@ ${CTAGS_OPTS}
|
||||||
|
@ -77,7 +77,7 @@ ERR_IDX_GEN = $(RPATH_VAR2_T_$(CFG_BUILD)_H_$(CFG_BUILD)) $(ERR_IDX_GEN_EXE)
|
|||||||
|
|
||||||
D := $(S)src/doc
|
D := $(S)src/doc
|
||||||
|
|
||||||
DOC_TARGETS := trpl style error-index
|
DOC_TARGETS := trpl nomicon style error-index
|
||||||
COMPILER_DOC_TARGETS :=
|
COMPILER_DOC_TARGETS :=
|
||||||
DOC_L10N_TARGETS :=
|
DOC_L10N_TARGETS :=
|
||||||
|
|
||||||
@ -287,6 +287,13 @@ doc/book/index.html: $(RUSTBOOK_EXE) $(wildcard $(S)/src/doc/trpl/*.md) | doc/
|
|||||||
$(Q)rm -rf doc/book
|
$(Q)rm -rf doc/book
|
||||||
$(Q)$(RUSTBOOK) build $(S)src/doc/trpl doc/book
|
$(Q)$(RUSTBOOK) build $(S)src/doc/trpl doc/book
|
||||||
|
|
||||||
|
nomicon: doc/nomicon/index.html
|
||||||
|
|
||||||
|
doc/nomicon/index.html: $(RUSTBOOK_EXE) $(wildcard $(S)/src/doc/nomicon/*.md) | doc/
|
||||||
|
@$(call E, rustbook: $@)
|
||||||
|
$(Q)rm -rf doc/nomicon
|
||||||
|
$(Q)$(RUSTBOOK) build $(S)src/doc/nomicon doc/nomicon
|
||||||
|
|
||||||
style: doc/style/index.html
|
style: doc/style/index.html
|
||||||
|
|
||||||
doc/style/index.html: $(RUSTBOOK_EXE) $(wildcard $(S)/src/doc/style/*.md) | doc/
|
doc/style/index.html: $(RUSTBOOK_EXE) $(wildcard $(S)/src/doc/style/*.md) | doc/
|
||||||
|
@ -13,12 +13,12 @@
|
|||||||
######################################################################
|
######################################################################
|
||||||
|
|
||||||
# The version number
|
# The version number
|
||||||
CFG_RELEASE_NUM=1.2.0
|
CFG_RELEASE_NUM=1.3.0
|
||||||
|
|
||||||
# An optional number to put after the label, e.g. '.2' -> '-beta.2'
|
# An optional number to put after the label, e.g. '.2' -> '-beta.2'
|
||||||
# NB Make sure it starts with a dot to conform to semver pre-release
|
# NB Make sure it starts with a dot to conform to semver pre-release
|
||||||
# versions (section 9)
|
# versions (section 9)
|
||||||
CFG_PRERELEASE_VERSION=.6
|
CFG_PRERELEASE_VERSION=.3
|
||||||
|
|
||||||
# Append a version-dependent hash to each library, so we can install different
|
# Append a version-dependent hash to each library, so we can install different
|
||||||
# versions in the same place
|
# versions in the same place
|
||||||
@ -295,7 +295,6 @@ LLVM_BINDIR_$(1)=$$(shell "$$(LLVM_CONFIG_$(1))" --bindir)
|
|||||||
LLVM_INCDIR_$(1)=$$(shell "$$(LLVM_CONFIG_$(1))" --includedir)
|
LLVM_INCDIR_$(1)=$$(shell "$$(LLVM_CONFIG_$(1))" --includedir)
|
||||||
LLVM_LIBDIR_$(1)=$$(shell "$$(LLVM_CONFIG_$(1))" --libdir)
|
LLVM_LIBDIR_$(1)=$$(shell "$$(LLVM_CONFIG_$(1))" --libdir)
|
||||||
LLVM_LIBDIR_RUSTFLAGS_$(1)=-L "$$(LLVM_LIBDIR_$(1))"
|
LLVM_LIBDIR_RUSTFLAGS_$(1)=-L "$$(LLVM_LIBDIR_$(1))"
|
||||||
LLVM_LIBS_$(1)=$$(shell "$$(LLVM_CONFIG_$(1))" --libs $$(LLVM_COMPONENTS))
|
|
||||||
LLVM_LDFLAGS_$(1)=$$(shell "$$(LLVM_CONFIG_$(1))" --ldflags)
|
LLVM_LDFLAGS_$(1)=$$(shell "$$(LLVM_CONFIG_$(1))" --ldflags)
|
||||||
ifeq ($$(findstring freebsd,$(1)),freebsd)
|
ifeq ($$(findstring freebsd,$(1)),freebsd)
|
||||||
# On FreeBSD, it may search wrong headers (that are for pre-installed LLVM),
|
# On FreeBSD, it may search wrong headers (that are for pre-installed LLVM),
|
||||||
|
@ -238,3 +238,56 @@ endef
|
|||||||
|
|
||||||
$(foreach target,$(CFG_TARGET), \
|
$(foreach target,$(CFG_TARGET), \
|
||||||
$(eval $(call CFG_MAKE_TOOLCHAIN,$(target))))
|
$(eval $(call CFG_MAKE_TOOLCHAIN,$(target))))
|
||||||
|
|
||||||
|
# There are more comments about this available in the target specification for
|
||||||
|
# Windows MSVC in the compiler, but the gist of it is that we use `llvm-ar.exe`
|
||||||
|
# instead of `lib.exe` for assembling archives, so we need to inject this custom
|
||||||
|
# dependency here.
|
||||||
|
define ADD_LLVM_AR_TO_MSVC_DEPS
|
||||||
|
ifeq ($$(findstring msvc,$(1)),msvc)
|
||||||
|
NATIVE_TOOL_DEPS_core_T_$(1) += llvm-ar.exe
|
||||||
|
INSTALLED_BINS_$(1) += llvm-ar.exe
|
||||||
|
endif
|
||||||
|
endef
|
||||||
|
|
||||||
|
$(foreach target,$(CFG_TARGET), \
|
||||||
|
$(eval $(call ADD_LLVM_AR_TO_MSVC_DEPS,$(target))))
|
||||||
|
|
||||||
|
# When working with MSVC on windows, each DLL needs to explicitly declare its
|
||||||
|
# interface to the outside world through some means. The options for doing so
|
||||||
|
# include:
|
||||||
|
#
|
||||||
|
# 1. A custom attribute on each function itself
|
||||||
|
# 2. A linker argument saying what to export
|
||||||
|
# 3. A file which lists all symbols that need to be exported
|
||||||
|
#
|
||||||
|
# The Rust compiler takes care (1) for us for all Rust code by annotating all
|
||||||
|
# public-facing functions with dllexport, but we have a few native dependencies
|
||||||
|
# which need to cross the DLL boundary. The most important of these dependencies
|
||||||
|
# is LLVM which is linked into `rustc_llvm.dll` but primarily used from
|
||||||
|
# `rustc_trans.dll`. This means that many of LLVM's C API functions need to be
|
||||||
|
# exposed from `rustc_llvm.dll` to be forwarded over the boundary.
|
||||||
|
#
|
||||||
|
# Unfortunately, at this time, LLVM does not handle this sort of exportation on
|
||||||
|
# Windows for us, so we're forced to do it ourselves if we want it (which seems
|
||||||
|
# like the path of least resistance right now). To do this we generate a `.DEF`
|
||||||
|
# file [1] which we then custom-pass to the linker when building the rustc_llvm
|
||||||
|
# crate. This DEF file list all symbols that are exported from
|
||||||
|
# `src/librustc_llvm/lib.rs` and is generated by a small python script.
|
||||||
|
#
|
||||||
|
# Fun times!
|
||||||
|
#
|
||||||
|
# [1]: https://msdn.microsoft.com/en-us/library/28d6s79h.aspx
|
||||||
|
define ADD_RUSTC_LLVM_DEF_TO_MSVC
|
||||||
|
ifeq ($$(findstring msvc,$(1)),msvc)
|
||||||
|
RUSTFLAGS_rustc_llvm_T_$(1) += -C link-args="-DEF:$(1)/rt/rustc_llvm.def"
|
||||||
|
CUSTOM_DEPS_rustc_llvm_T_$(1) += $(1)/rt/rustc_llvm.def
|
||||||
|
|
||||||
|
$(1)/rt/rustc_llvm.def: $$(S)src/etc/mklldef.py $$(S)src/librustc_llvm/lib.rs
|
||||||
|
$$(CFG_PYTHON) $$^ $$@ rustc_llvm-$$(CFG_FILENAME_EXTRA)
|
||||||
|
endif
|
||||||
|
endef
|
||||||
|
|
||||||
|
$(foreach target,$(CFG_TARGET), \
|
||||||
|
$(eval $(call ADD_RUSTC_LLVM_DEF_TO_MSVC,$(target))))
|
||||||
|
|
||||||
|
28
mk/rt.mk
28
mk/rt.mk
@ -53,9 +53,7 @@ NATIVE_DEPS_hoedown_$(1) := hoedown/src/autolink.c \
|
|||||||
NATIVE_DEPS_miniz_$(1) = miniz.c
|
NATIVE_DEPS_miniz_$(1) = miniz.c
|
||||||
NATIVE_DEPS_rust_builtin_$(1) := rust_builtin.c \
|
NATIVE_DEPS_rust_builtin_$(1) := rust_builtin.c \
|
||||||
rust_android_dummy.c
|
rust_android_dummy.c
|
||||||
NATIVE_DEPS_rustrt_native_$(1) := \
|
NATIVE_DEPS_rustrt_native_$(1) := arch/$$(HOST_$(1))/record_sp.S
|
||||||
rust_try.ll \
|
|
||||||
arch/$$(HOST_$(1))/record_sp.S
|
|
||||||
NATIVE_DEPS_rust_test_helpers_$(1) := rust_test_helpers.c
|
NATIVE_DEPS_rust_test_helpers_$(1) := rust_test_helpers.c
|
||||||
NATIVE_DEPS_morestack_$(1) := arch/$$(HOST_$(1))/morestack.S
|
NATIVE_DEPS_morestack_$(1) := arch/$$(HOST_$(1))/morestack.S
|
||||||
|
|
||||||
@ -69,14 +67,6 @@ NATIVE_DEPS_morestack_$(1) := arch/$$(HOST_$(1))/morestack.S
|
|||||||
|
|
||||||
RT_OUTPUT_DIR_$(1) := $(1)/rt
|
RT_OUTPUT_DIR_$(1) := $(1)/rt
|
||||||
|
|
||||||
$$(RT_OUTPUT_DIR_$(1))/%.o: $(S)src/rt/%.ll $$(MKFILE_DEPS) \
|
|
||||||
$$(LLVM_CONFIG_$$(CFG_BUILD))
|
|
||||||
@mkdir -p $$(@D)
|
|
||||||
@$$(call E, compile: $$@)
|
|
||||||
$$(Q)$$(LLC_$$(CFG_BUILD)) $$(CFG_LLC_FLAGS_$(1)) \
|
|
||||||
-filetype=obj -mtriple=$$(CFG_LLVM_TARGET_$(1)) \
|
|
||||||
-relocation-model=pic -o $$@ $$<
|
|
||||||
|
|
||||||
$$(RT_OUTPUT_DIR_$(1))/%.o: $(S)src/rt/%.c $$(MKFILE_DEPS)
|
$$(RT_OUTPUT_DIR_$(1))/%.o: $(S)src/rt/%.c $$(MKFILE_DEPS)
|
||||||
@mkdir -p $$(@D)
|
@mkdir -p $$(@D)
|
||||||
@$$(call E, compile: $$@)
|
@$$(call E, compile: $$@)
|
||||||
@ -90,6 +80,17 @@ $$(RT_OUTPUT_DIR_$(1))/%.o: $(S)src/rt/%.S $$(MKFILE_DEPS) \
|
|||||||
@mkdir -p $$(@D)
|
@mkdir -p $$(@D)
|
||||||
@$$(call E, compile: $$@)
|
@$$(call E, compile: $$@)
|
||||||
$$(Q)$$(call CFG_ASSEMBLE_$(1),$$@,$$<)
|
$$(Q)$$(call CFG_ASSEMBLE_$(1),$$@,$$<)
|
||||||
|
|
||||||
|
# On MSVC targets the compiler's default include path (e.g. where to find system
|
||||||
|
# headers) is specified by the INCLUDE environment variable. This may not be set
|
||||||
|
# so the ./configure script scraped the relevant values and this is the location
|
||||||
|
# that we put them into cl.exe's environment.
|
||||||
|
ifeq ($$(findstring msvc,$(1)),msvc)
|
||||||
|
$$(RT_OUTPUT_DIR_$(1))/%.o: \
|
||||||
|
export INCLUDE := $$(CFG_MSVC_INCLUDE_PATH_$$(HOST_$(1)))
|
||||||
|
$(1)/rustllvm/%.o: \
|
||||||
|
export INCLUDE := $$(CFG_MSVC_INCLUDE_PATH_$$(HOST_$(1)))
|
||||||
|
endif
|
||||||
endef
|
endef
|
||||||
|
|
||||||
$(foreach target,$(CFG_TARGET),$(eval $(call NATIVE_LIBRARIES,$(target))))
|
$(foreach target,$(CFG_TARGET),$(eval $(call NATIVE_LIBRARIES,$(target))))
|
||||||
@ -104,7 +105,6 @@ define THIRD_PARTY_LIB
|
|||||||
OBJS_$(2)_$(1) := $$(NATIVE_DEPS_$(2)_$(1):%=$$(RT_OUTPUT_DIR_$(1))/%)
|
OBJS_$(2)_$(1) := $$(NATIVE_DEPS_$(2)_$(1):%=$$(RT_OUTPUT_DIR_$(1))/%)
|
||||||
OBJS_$(2)_$(1) := $$(OBJS_$(2)_$(1):.c=.o)
|
OBJS_$(2)_$(1) := $$(OBJS_$(2)_$(1):.c=.o)
|
||||||
OBJS_$(2)_$(1) := $$(OBJS_$(2)_$(1):.cpp=.o)
|
OBJS_$(2)_$(1) := $$(OBJS_$(2)_$(1):.cpp=.o)
|
||||||
OBJS_$(2)_$(1) := $$(OBJS_$(2)_$(1):.ll=.o)
|
|
||||||
OBJS_$(2)_$(1) := $$(OBJS_$(2)_$(1):.S=.o)
|
OBJS_$(2)_$(1) := $$(OBJS_$(2)_$(1):.S=.o)
|
||||||
NATIVE_$(2)_$(1) := $$(call CFG_STATIC_LIB_NAME_$(1),$(2))
|
NATIVE_$(2)_$(1) := $$(call CFG_STATIC_LIB_NAME_$(1),$(2))
|
||||||
$$(RT_OUTPUT_DIR_$(1))/$$(NATIVE_$(2)_$(1)): $$(OBJS_$(2)_$(1))
|
$$(RT_OUTPUT_DIR_$(1))/$$(NATIVE_$(2)_$(1)): $$(OBJS_$(2)_$(1))
|
||||||
@ -237,8 +237,12 @@ COMPRT_CFLAGS_$(1) := $$(CFG_GCCISH_CFLAGS_$(1))
|
|||||||
ifeq ($$(findstring msvc,$(1)),msvc)
|
ifeq ($$(findstring msvc,$(1)),msvc)
|
||||||
COMPRT_CC_$(1) := gcc
|
COMPRT_CC_$(1) := gcc
|
||||||
COMPRT_AR_$(1) := ar
|
COMPRT_AR_$(1) := ar
|
||||||
|
ifeq ($$(findstring i686,$(1)),i686)
|
||||||
|
COMPRT_CFLAGS_$(1) := $$(CFG_GCCISH_CFLAGS_$(1)) -m32
|
||||||
|
else
|
||||||
COMPRT_CFLAGS_$(1) := $$(CFG_GCCISH_CFLAGS_$(1)) -m64
|
COMPRT_CFLAGS_$(1) := $$(CFG_GCCISH_CFLAGS_$(1)) -m64
|
||||||
endif
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
$$(COMPRT_LIB_$(1)): $$(COMPRT_DEPS) $$(MKFILE_DEPS)
|
$$(COMPRT_LIB_$(1)): $$(COMPRT_DEPS) $$(MKFILE_DEPS)
|
||||||
@$$(call E, make: compiler-rt)
|
@$$(call E, make: compiler-rt)
|
||||||
|
@ -24,7 +24,8 @@ LLVM_EXTRA_INCDIRS_$(1)= $$(call CFG_CC_INCLUDE_$(1),$(S)src/llvm/include) \
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
RUSTLLVM_OBJS_CS_$(1) := $$(addprefix rustllvm/, \
|
RUSTLLVM_OBJS_CS_$(1) := $$(addprefix rustllvm/, \
|
||||||
ExecutionEngineWrapper.cpp RustWrapper.cpp PassWrapper.cpp)
|
ExecutionEngineWrapper.cpp RustWrapper.cpp PassWrapper.cpp \
|
||||||
|
ArchiveWrapper.cpp)
|
||||||
|
|
||||||
RUSTLLVM_INCS_$(1) = $$(LLVM_EXTRA_INCDIRS_$(1)) \
|
RUSTLLVM_INCS_$(1) = $$(LLVM_EXTRA_INCDIRS_$(1)) \
|
||||||
$$(call CFG_CC_INCLUDE_$(1),$$(LLVM_INCDIR_$(1))) \
|
$$(call CFG_CC_INCLUDE_$(1),$$(LLVM_INCDIR_$(1))) \
|
||||||
|
35
mk/target.mk
35
mk/target.mk
@ -220,3 +220,38 @@ $(foreach target,$(CFG_TARGET), \
|
|||||||
$(foreach crate,$(CRATES), \
|
$(foreach crate,$(CRATES), \
|
||||||
$(foreach tool,$(NATIVE_TOOL_DEPS_$(crate)_T_$(target)), \
|
$(foreach tool,$(NATIVE_TOOL_DEPS_$(crate)_T_$(target)), \
|
||||||
$(eval $(call MOVE_TOOLS_TO_SNAPSHOT_HOST_DIR,0,$(target),$(BOOTSTRAP_FROM_$(target)),$(crate),$(tool))))))
|
$(eval $(call MOVE_TOOLS_TO_SNAPSHOT_HOST_DIR,0,$(target),$(BOOTSTRAP_FROM_$(target)),$(crate),$(tool))))))
|
||||||
|
|
||||||
|
# For MSVC targets we need to set up some environment variables for the linker
|
||||||
|
# to work correctly when building Rust crates. These two variables are:
|
||||||
|
#
|
||||||
|
# - LIB tells the linker the default search path for finding system libraries,
|
||||||
|
# for example kernel32.dll
|
||||||
|
# - PATH needs to be modified to ensure that MSVC's link.exe is first in the
|
||||||
|
# path instead of MinGW's /usr/bin/link.exe (entirely unrelated)
|
||||||
|
#
|
||||||
|
# The values for these variables are detected by the configure script.
|
||||||
|
define SETUP_LIB_MSVC_ENV_VARS
|
||||||
|
ifeq ($$(findstring msvc,$(2)),msvc)
|
||||||
|
$$(TLIB$(1)_T_$(2)_H_$(3))/stamp.$(4): \
|
||||||
|
export LIB := $$(CFG_MSVC_LIB_PATH_$$(HOST_$(2)))
|
||||||
|
$$(TLIB$(1)_T_$(2)_H_$(3))/stamp.$(4): \
|
||||||
|
export PATH := $$(CFG_MSVC_BINDIR_$$(HOST_$(2))):$$(PATH)
|
||||||
|
endif
|
||||||
|
endef
|
||||||
|
define SETUP_TOOL_MSVC_ENV_VARS
|
||||||
|
ifeq ($$(findstring msvc,$(2)),msvc)
|
||||||
|
$$(TBIN$(1)_T_$(2)_H_$(3))/$(4)$$(X_$(2)): \
|
||||||
|
export LIB := $$(CFG_MSVC_LIB_PATH_$$(HOST_$(2)))
|
||||||
|
$$(TBIN$(1)_T_$(2)_H_$(3))/$(4)$$(X_$(2)): \
|
||||||
|
export PATH := $$(CFG_MSVC_BINDIR_$$(HOST_$(2))):$$(PATH)
|
||||||
|
endif
|
||||||
|
endef
|
||||||
|
|
||||||
|
$(foreach host,$(CFG_HOST), \
|
||||||
|
$(foreach target,$(CFG_TARGET), \
|
||||||
|
$(foreach crate,$(CRATES), \
|
||||||
|
$(eval $(call SETUP_LIB_MSVC_ENV_VARS,0,$(target),$(host),$(crate))))))
|
||||||
|
$(foreach host,$(CFG_HOST), \
|
||||||
|
$(foreach target,$(CFG_TARGET), \
|
||||||
|
$(foreach tool,$(TOOLS), \
|
||||||
|
$(eval $(call SETUP_TOOL_MSVC_ENV_VARS,0,$(target),$(host),$(tool))))))
|
||||||
|
@ -162,7 +162,8 @@ $(foreach doc,$(DOCS), \
|
|||||||
$(eval $(call DOCTEST,md-$(doc),$(S)src/doc/$(doc).md)))
|
$(eval $(call DOCTEST,md-$(doc),$(S)src/doc/$(doc).md)))
|
||||||
$(foreach file,$(wildcard $(S)src/doc/trpl/*.md), \
|
$(foreach file,$(wildcard $(S)src/doc/trpl/*.md), \
|
||||||
$(eval $(call DOCTEST,$(file:$(S)src/doc/trpl/%.md=trpl-%),$(file))))
|
$(eval $(call DOCTEST,$(file:$(S)src/doc/trpl/%.md=trpl-%),$(file))))
|
||||||
|
$(foreach file,$(wildcard $(S)src/doc/nomicon/*.md), \
|
||||||
|
$(eval $(call DOCTEST,$(file:$(S)src/doc/nomicon/%.md=nomicon-%),$(file))))
|
||||||
######################################################################
|
######################################################################
|
||||||
# Main test targets
|
# Main test targets
|
||||||
######################################################################
|
######################################################################
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
#![feature(libc)]
|
#![feature(libc)]
|
||||||
#![feature(path_ext)]
|
#![feature(path_ext)]
|
||||||
#![feature(rustc_private)]
|
#![feature(rustc_private)]
|
||||||
#![feature(slice_extras)]
|
#![feature(slice_splits)]
|
||||||
#![feature(str_char)]
|
#![feature(str_char)]
|
||||||
#![feature(test)]
|
#![feature(test)]
|
||||||
#![feature(vec_push_all)]
|
#![feature(vec_push_all)]
|
||||||
@ -90,9 +90,7 @@ pub fn parse_config(args: Vec<String> ) -> Config {
|
|||||||
optopt("", "lldb-python-dir", "directory containing LLDB's python module", "PATH"),
|
optopt("", "lldb-python-dir", "directory containing LLDB's python module", "PATH"),
|
||||||
optflag("h", "help", "show this message"));
|
optflag("h", "help", "show this message"));
|
||||||
|
|
||||||
assert!(!args.is_empty());
|
let (argv0, args_) = args.split_first().unwrap();
|
||||||
let argv0 = args[0].clone();
|
|
||||||
let args_ = args.tail();
|
|
||||||
if args[1] == "-h" || args[1] == "--help" {
|
if args[1] == "-h" || args[1] == "--help" {
|
||||||
let message = format!("Usage: {} [OPTIONS] [TESTNAME...]", argv0);
|
let message = format!("Usage: {} [OPTIONS] [TESTNAME...]", argv0);
|
||||||
println!("{}", getopts::usage(&message, &groups));
|
println!("{}", getopts::usage(&message, &groups));
|
||||||
|
@ -344,7 +344,7 @@ fn run_debuginfo_gdb_test(config: &Config, props: &TestProps, testfile: &Path) {
|
|||||||
check_lines,
|
check_lines,
|
||||||
breakpoint_lines
|
breakpoint_lines
|
||||||
} = parse_debugger_commands(testfile, "gdb");
|
} = parse_debugger_commands(testfile, "gdb");
|
||||||
let mut cmds = commands.connect("\n");
|
let mut cmds = commands.join("\n");
|
||||||
|
|
||||||
// compile test file (it should have 'compile-flags:-g' in the header)
|
// compile test file (it should have 'compile-flags:-g' in the header)
|
||||||
let compiler_run_result = compile_test(config, props, testfile);
|
let compiler_run_result = compile_test(config, props, testfile);
|
||||||
@ -799,7 +799,7 @@ fn cleanup_debug_info_options(options: &Option<String>) -> Option<String> {
|
|||||||
split_maybe_args(options).into_iter()
|
split_maybe_args(options).into_iter()
|
||||||
.filter(|x| !options_to_remove.contains(x))
|
.filter(|x| !options_to_remove.contains(x))
|
||||||
.collect::<Vec<String>>()
|
.collect::<Vec<String>>()
|
||||||
.connect(" ");
|
.join(" ");
|
||||||
Some(new_options)
|
Some(new_options)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1126,16 +1126,10 @@ impl fmt::Display for Status {
|
|||||||
|
|
||||||
fn compile_test(config: &Config, props: &TestProps,
|
fn compile_test(config: &Config, props: &TestProps,
|
||||||
testfile: &Path) -> ProcRes {
|
testfile: &Path) -> ProcRes {
|
||||||
compile_test_(config, props, testfile, &[])
|
|
||||||
}
|
|
||||||
|
|
||||||
fn compile_test_(config: &Config, props: &TestProps,
|
|
||||||
testfile: &Path, extra_args: &[String]) -> ProcRes {
|
|
||||||
let aux_dir = aux_output_dir_name(config, testfile);
|
let aux_dir = aux_output_dir_name(config, testfile);
|
||||||
// FIXME (#9639): This needs to handle non-utf8 paths
|
// FIXME (#9639): This needs to handle non-utf8 paths
|
||||||
let mut link_args = vec!("-L".to_string(),
|
let link_args = vec!("-L".to_string(),
|
||||||
aux_dir.to_str().unwrap().to_string());
|
aux_dir.to_str().unwrap().to_string());
|
||||||
link_args.extend(extra_args.iter().cloned());
|
|
||||||
let args = make_compile_args(config,
|
let args = make_compile_args(config,
|
||||||
props,
|
props,
|
||||||
link_args,
|
link_args,
|
||||||
@ -1144,7 +1138,7 @@ fn compile_test_(config: &Config, props: &TestProps,
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn document(config: &Config, props: &TestProps,
|
fn document(config: &Config, props: &TestProps,
|
||||||
testfile: &Path, extra_args: &[String]) -> (ProcRes, PathBuf) {
|
testfile: &Path) -> (ProcRes, PathBuf) {
|
||||||
let aux_dir = aux_output_dir_name(config, testfile);
|
let aux_dir = aux_output_dir_name(config, testfile);
|
||||||
let out_dir = output_base_name(config, testfile);
|
let out_dir = output_base_name(config, testfile);
|
||||||
let _ = fs::remove_dir_all(&out_dir);
|
let _ = fs::remove_dir_all(&out_dir);
|
||||||
@ -1154,7 +1148,6 @@ fn document(config: &Config, props: &TestProps,
|
|||||||
"-o".to_string(),
|
"-o".to_string(),
|
||||||
out_dir.to_str().unwrap().to_string(),
|
out_dir.to_str().unwrap().to_string(),
|
||||||
testfile.to_str().unwrap().to_string()];
|
testfile.to_str().unwrap().to_string()];
|
||||||
args.extend(extra_args.iter().cloned());
|
|
||||||
args.extend(split_maybe_args(&props.compile_flags));
|
args.extend(split_maybe_args(&props.compile_flags));
|
||||||
let args = ProcArgs {
|
let args = ProcArgs {
|
||||||
prog: config.rustdoc_path.to_str().unwrap().to_string(),
|
prog: config.rustdoc_path.to_str().unwrap().to_string(),
|
||||||
@ -1419,7 +1412,7 @@ fn make_cmdline(libpath: &str, prog: &str, args: &[String]) -> String {
|
|||||||
|
|
||||||
// Linux and mac don't require adjusting the library search path
|
// Linux and mac don't require adjusting the library search path
|
||||||
if cfg!(unix) {
|
if cfg!(unix) {
|
||||||
format!("{} {}", prog, args.connect(" "))
|
format!("{} {}", prog, args.join(" "))
|
||||||
} else {
|
} else {
|
||||||
// Build the LD_LIBRARY_PATH variable as it would be seen on the command line
|
// Build the LD_LIBRARY_PATH variable as it would be seen on the command line
|
||||||
// for diagnostic purposes
|
// for diagnostic purposes
|
||||||
@ -1427,7 +1420,7 @@ fn make_cmdline(libpath: &str, prog: &str, args: &[String]) -> String {
|
|||||||
format!("{}=\"{}\"", util::lib_path_env_var(), util::make_new_path(path))
|
format!("{}=\"{}\"", util::lib_path_env_var(), util::make_new_path(path))
|
||||||
}
|
}
|
||||||
|
|
||||||
format!("{} {} {}", lib_path_cmd_prefix(libpath), prog, args.connect(" "))
|
format!("{} {} {}", lib_path_cmd_prefix(libpath), prog, args.join(" "))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1709,15 +1702,18 @@ fn run_codegen_test(config: &Config, props: &TestProps, testfile: &Path) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn charset() -> &'static str {
|
fn charset() -> &'static str {
|
||||||
if cfg!(any(target_os = "bitrig", target_os = "freebsd")) {
|
// FreeBSD 10.1 defaults to GDB 6.1.1 which doesn't support "auto" charset
|
||||||
|
if cfg!(target_os = "bitrig") {
|
||||||
"auto"
|
"auto"
|
||||||
|
} else if cfg!(target_os = "freebsd") {
|
||||||
|
"ISO-8859-1"
|
||||||
} else {
|
} else {
|
||||||
"UTF-8"
|
"UTF-8"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_rustdoc_test(config: &Config, props: &TestProps, testfile: &Path) {
|
fn run_rustdoc_test(config: &Config, props: &TestProps, testfile: &Path) {
|
||||||
let (proc_res, out_dir) = document(config, props, testfile, &[]);
|
let (proc_res, out_dir) = document(config, props, testfile);
|
||||||
if !proc_res.status.success() {
|
if !proc_res.status.success() {
|
||||||
fatal_proc_rec("rustdoc failed!", &proc_res);
|
fatal_proc_rec("rustdoc failed!", &proc_res);
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@ const OS_TABLE: &'static [(&'static str, &'static str)] = &[
|
|||||||
("ios", "ios"),
|
("ios", "ios"),
|
||||||
("linux", "linux"),
|
("linux", "linux"),
|
||||||
("mingw32", "windows"),
|
("mingw32", "windows"),
|
||||||
|
("netbsd", "netbsd"),
|
||||||
("openbsd", "openbsd"),
|
("openbsd", "openbsd"),
|
||||||
("win32", "windows"),
|
("win32", "windows"),
|
||||||
("windows", "windows"),
|
("windows", "windows"),
|
||||||
|
@ -99,7 +99,7 @@ Second, it makes cost explicit. In general, the only safe way to have a
|
|||||||
non-exhaustive match would be to panic the thread if nothing is matched, though
|
non-exhaustive match would be to panic the thread if nothing is matched, though
|
||||||
it could fall through if the type of the `match` expression is `()`. This sort
|
it could fall through if the type of the `match` expression is `()`. This sort
|
||||||
of hidden cost and special casing is against the language's philosophy. It's
|
of hidden cost and special casing is against the language's philosophy. It's
|
||||||
easy to ignore certain cases by using the `_` wildcard:
|
easy to ignore all unspecified cases by using the `_` wildcard:
|
||||||
|
|
||||||
```rust,ignore
|
```rust,ignore
|
||||||
match val.do_something() {
|
match val.do_something() {
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
% The (old) Rust Pointer Guide
|
% The Rust Pointer Guide
|
||||||
|
|
||||||
This content has moved into
|
This content has been removed, with no direct replacement. Rust only
|
||||||
[the Rust Programming Language book](book/pointers.html).
|
has two built-in pointer types now,
|
||||||
|
[references](book/references-and-borrowing.html) and [raw
|
||||||
|
pointers](book/raw-pointers.html). Older Rusts had many more pointer
|
||||||
|
types, they’re gone now.
|
||||||
|
@ -20,6 +20,13 @@ series of small examples.
|
|||||||
|
|
||||||
[rbe]: http://rustbyexample.com/
|
[rbe]: http://rustbyexample.com/
|
||||||
|
|
||||||
|
# The Standard Library
|
||||||
|
|
||||||
|
We have [API documentation for the entire standard
|
||||||
|
library](std/index.html). There's a list of crates on the left with more
|
||||||
|
specific sections, or you can use the search bar at the top to search for
|
||||||
|
something if you know its name.
|
||||||
|
|
||||||
# Community & Getting Help
|
# Community & Getting Help
|
||||||
|
|
||||||
If you need help with something, or just want to talk about Rust with others,
|
If you need help with something, or just want to talk about Rust with others,
|
||||||
@ -75,13 +82,6 @@ There are questions that are asked quite often, so we've made FAQs for them:
|
|||||||
* [Project FAQ](complement-project-faq.html)
|
* [Project FAQ](complement-project-faq.html)
|
||||||
* [How to submit a bug report](https://github.com/rust-lang/rust/blob/master/CONTRIBUTING.md#bug-reports)
|
* [How to submit a bug report](https://github.com/rust-lang/rust/blob/master/CONTRIBUTING.md#bug-reports)
|
||||||
|
|
||||||
# The Standard Library
|
|
||||||
|
|
||||||
We have [API documentation for the entire standard
|
|
||||||
library](std/index.html). There's a list of crates on the left with more
|
|
||||||
specific sections, or you can use the search bar at the top to search for
|
|
||||||
something if you know its name.
|
|
||||||
|
|
||||||
# The Error Index
|
# The Error Index
|
||||||
|
|
||||||
If you encounter an error while compiling your code you may be able to look it
|
If you encounter an error while compiling your code you may be able to look it
|
||||||
|
38
src/doc/nomicon/README.md
Normal file
38
src/doc/nomicon/README.md
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
% The Rustonomicon
|
||||||
|
|
||||||
|
#### The Dark Arts of Advanced and Unsafe Rust Programming
|
||||||
|
|
||||||
|
# NOTE: This is a draft document, and may contain serious errors
|
||||||
|
|
||||||
|
> Instead of the programs I had hoped for, there came only a shuddering blackness
|
||||||
|
and ineffable loneliness; and I saw at last a fearful truth which no one had
|
||||||
|
ever dared to breathe before — the unwhisperable secret of secrets — The fact
|
||||||
|
that this language of stone and stridor is not a sentient perpetuation of Rust
|
||||||
|
as London is of Old London and Paris of Old Paris, but that it is in fact
|
||||||
|
quite unsafe, its sprawling body imperfectly embalmed and infested with queer
|
||||||
|
animate things which have nothing to do with it as it was in compilation.
|
||||||
|
|
||||||
|
This book digs into all the awful details that are necessary to understand in
|
||||||
|
order to write correct Unsafe Rust programs. Due to the nature of this problem,
|
||||||
|
it may lead to unleashing untold horrors that shatter your psyche into a billion
|
||||||
|
infinitesimal fragments of despair.
|
||||||
|
|
||||||
|
Should you wish a long and happy career of writing Rust programs, you should
|
||||||
|
turn back now and forget you ever saw this book. It is not necessary. However
|
||||||
|
if you intend to write unsafe code -- or just want to dig into the guts of the
|
||||||
|
language -- this book contains invaluable information.
|
||||||
|
|
||||||
|
Unlike [The Book][trpl] we will be assuming considerable prior knowledge. In
|
||||||
|
particular, you should be comfortable with basic systems programming and Rust.
|
||||||
|
If you don't feel comfortable with these topics, you should consider [reading
|
||||||
|
The Book][trpl] first. Though we will not be assuming that you have, and will
|
||||||
|
take care to occasionally give a refresher on the basics where appropriate. You
|
||||||
|
can skip straight to this book if you want; just know that we won't be
|
||||||
|
explaining everything from the ground up.
|
||||||
|
|
||||||
|
To be clear, this book goes into deep detail. We're going to dig into
|
||||||
|
exception-safety, pointer aliasing, memory models, and even some type-theory.
|
||||||
|
We will also be spending a lot of time talking about the different kinds
|
||||||
|
of safety and guarantees.
|
||||||
|
|
||||||
|
[trpl]: ../book/
|
53
src/doc/nomicon/SUMMARY.md
Normal file
53
src/doc/nomicon/SUMMARY.md
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
# Summary
|
||||||
|
|
||||||
|
* [Meet Safe and Unsafe](meet-safe-and-unsafe.md)
|
||||||
|
* [How Safe and Unsafe Interact](safe-unsafe-meaning.md)
|
||||||
|
* [Working with Unsafe](working-with-unsafe.md)
|
||||||
|
* [Data Layout](data.md)
|
||||||
|
* [repr(Rust)](repr-rust.md)
|
||||||
|
* [Exotically Sized Types](exotic-sizes.md)
|
||||||
|
* [Other reprs](other-reprs.md)
|
||||||
|
* [Ownership](ownership.md)
|
||||||
|
* [References](references.md)
|
||||||
|
* [Lifetimes](lifetimes.md)
|
||||||
|
* [Limits of Lifetimes](lifetime-mismatch.md)
|
||||||
|
* [Lifetime Elision](lifetime-elision.md)
|
||||||
|
* [Unbounded Lifetimes](unbounded-lifetimes.md)
|
||||||
|
* [Higher-Rank Trait Bounds](hrtb.md)
|
||||||
|
* [Subtyping and Variance](subtyping.md)
|
||||||
|
* [Drop Check](dropck.md)
|
||||||
|
* [PhantomData](phantom-data.md)
|
||||||
|
* [Splitting Borrows](borrow-splitting.md)
|
||||||
|
* [Type Conversions](conversions.md)
|
||||||
|
* [Coercions](coercions.md)
|
||||||
|
* [The Dot Operator](dot-operator.md)
|
||||||
|
* [Casts](casts.md)
|
||||||
|
* [Transmutes](transmutes.md)
|
||||||
|
* [Uninitialized Memory](uninitialized.md)
|
||||||
|
* [Checked](checked-uninit.md)
|
||||||
|
* [Drop Flags](drop-flags.md)
|
||||||
|
* [Unchecked](unchecked-uninit.md)
|
||||||
|
* [Ownership Based Resource Management](obrm.md)
|
||||||
|
* [Constructors](constructors.md)
|
||||||
|
* [Destructors](destructors.md)
|
||||||
|
* [Leaking](leaking.md)
|
||||||
|
* [Unwinding](unwinding.md)
|
||||||
|
* [Exception Safety](exception-safety.md)
|
||||||
|
* [Poisoning](poisoning.md)
|
||||||
|
* [Concurrency](concurrency.md)
|
||||||
|
* [Races](races.md)
|
||||||
|
* [Send and Sync](send-and-sync.md)
|
||||||
|
* [Atomics](atomics.md)
|
||||||
|
* [Implementing Vec](vec.md)
|
||||||
|
* [Layout](vec-layout.md)
|
||||||
|
* [Allocating](vec-alloc.md)
|
||||||
|
* [Push and Pop](vec-push-pop.md)
|
||||||
|
* [Deallocating](vec-dealloc.md)
|
||||||
|
* [Deref](vec-deref.md)
|
||||||
|
* [Insert and Remove](vec-insert-remove.md)
|
||||||
|
* [IntoIter](vec-into-iter.md)
|
||||||
|
* [RawVec](vec-raw.md)
|
||||||
|
* [Drain](vec-drain.md)
|
||||||
|
* [Handling Zero-Sized Types](vec-zsts.md)
|
||||||
|
* [Final Code](vec-final.md)
|
||||||
|
* [Implementing Arc and Mutex](arc-and-mutex.md)
|
7
src/doc/nomicon/arc-and-mutex.md
Normal file
7
src/doc/nomicon/arc-and-mutex.md
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
% Implementing Arc and Mutex
|
||||||
|
|
||||||
|
Knowing the theory is all fine and good, but the *best* way to understand
|
||||||
|
something is to use it. To better understand atomics and interior mutability,
|
||||||
|
we'll be implementing versions of the standard library's Arc and Mutex types.
|
||||||
|
|
||||||
|
TODO: ALL OF THIS OMG
|
255
src/doc/nomicon/atomics.md
Normal file
255
src/doc/nomicon/atomics.md
Normal file
@ -0,0 +1,255 @@
|
|||||||
|
% Atomics
|
||||||
|
|
||||||
|
Rust pretty blatantly just inherits C11's memory model for atomics. This is not
|
||||||
|
due this model being particularly excellent or easy to understand. Indeed, this
|
||||||
|
model is quite complex and known to have [several flaws][C11-busted]. Rather, it
|
||||||
|
is a pragmatic concession to the fact that *everyone* is pretty bad at modeling
|
||||||
|
atomics. At very least, we can benefit from existing tooling and research around
|
||||||
|
C.
|
||||||
|
|
||||||
|
Trying to fully explain the model in this book is fairly hopeless. It's defined
|
||||||
|
in terms of madness-inducing causality graphs that require a full book to
|
||||||
|
properly understand in a practical way. If you want all the nitty-gritty
|
||||||
|
details, you should check out [C's specification (Section 7.17)][C11-model].
|
||||||
|
Still, we'll try to cover the basics and some of the problems Rust developers
|
||||||
|
face.
|
||||||
|
|
||||||
|
The C11 memory model is fundamentally about trying to bridge the gap between the
|
||||||
|
semantics we want, the optimizations compilers want, and the inconsistent chaos
|
||||||
|
our hardware wants. *We* would like to just write programs and have them do
|
||||||
|
exactly what we said but, you know, fast. Wouldn't that be great?
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Compiler Reordering
|
||||||
|
|
||||||
|
Compilers fundamentally want to be able to do all sorts of crazy transformations
|
||||||
|
to reduce data dependencies and eliminate dead code. In particular, they may
|
||||||
|
radically change the actual order of events, or make events never occur! If we
|
||||||
|
write something like
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
x = 1;
|
||||||
|
y = 3;
|
||||||
|
x = 2;
|
||||||
|
```
|
||||||
|
|
||||||
|
The compiler may conclude that it would be best if your program did
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
x = 2;
|
||||||
|
y = 3;
|
||||||
|
```
|
||||||
|
|
||||||
|
This has inverted the order of events and completely eliminated one event.
|
||||||
|
From a single-threaded perspective this is completely unobservable: after all
|
||||||
|
the statements have executed we are in exactly the same state. But if our
|
||||||
|
program is multi-threaded, we may have been relying on `x` to actually be
|
||||||
|
assigned to 1 before `y` was assigned. We would like the compiler to be
|
||||||
|
able to make these kinds of optimizations, because they can seriously improve
|
||||||
|
performance. On the other hand, we'd also like to be able to depend on our
|
||||||
|
program *doing the thing we said*.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Hardware Reordering
|
||||||
|
|
||||||
|
On the other hand, even if the compiler totally understood what we wanted and
|
||||||
|
respected our wishes, our hardware might instead get us in trouble. Trouble
|
||||||
|
comes from CPUs in the form of memory hierarchies. There is indeed a global
|
||||||
|
shared memory space somewhere in your hardware, but from the perspective of each
|
||||||
|
CPU core it is *so very far away* and *so very slow*. Each CPU would rather work
|
||||||
|
with its local cache of the data and only go through all the anguish of
|
||||||
|
talking to shared memory only when it doesn't actually have that memory in
|
||||||
|
cache.
|
||||||
|
|
||||||
|
After all, that's the whole point of the cache, right? If every read from the
|
||||||
|
cache had to run back to shared memory to double check that it hadn't changed,
|
||||||
|
what would the point be? The end result is that the hardware doesn't guarantee
|
||||||
|
that events that occur in the same order on *one* thread, occur in the same
|
||||||
|
order on *another* thread. To guarantee this, we must issue special instructions
|
||||||
|
to the CPU telling it to be a bit less smart.
|
||||||
|
|
||||||
|
For instance, say we convince the compiler to emit this logic:
|
||||||
|
|
||||||
|
```text
|
||||||
|
initial state: x = 0, y = 1
|
||||||
|
|
||||||
|
THREAD 1 THREAD2
|
||||||
|
y = 3; if x == 1 {
|
||||||
|
x = 1; y *= 2;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Ideally this program has 2 possible final states:
|
||||||
|
|
||||||
|
* `y = 3`: (thread 2 did the check before thread 1 completed)
|
||||||
|
* `y = 6`: (thread 2 did the check after thread 1 completed)
|
||||||
|
|
||||||
|
However there's a third potential state that the hardware enables:
|
||||||
|
|
||||||
|
* `y = 2`: (thread 2 saw `x = 1`, but not `y = 3`, and then overwrote `y = 3`)
|
||||||
|
|
||||||
|
It's worth noting that different kinds of CPU provide different guarantees. It
|
||||||
|
is common to separate hardware into two categories: strongly-ordered and weakly-
|
||||||
|
ordered. Most notably x86/64 provides strong ordering guarantees, while ARM
|
||||||
|
provides weak ordering guarantees. This has two consequences for concurrent
|
||||||
|
programming:
|
||||||
|
|
||||||
|
* Asking for stronger guarantees on strongly-ordered hardware may be cheap or
|
||||||
|
even free because they already provide strong guarantees unconditionally.
|
||||||
|
Weaker guarantees may only yield performance wins on weakly-ordered hardware.
|
||||||
|
|
||||||
|
* Asking for guarantees that are too weak on strongly-ordered hardware is
|
||||||
|
more likely to *happen* to work, even though your program is strictly
|
||||||
|
incorrect. If possible, concurrent algorithms should be tested on
|
||||||
|
weakly-ordered hardware.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Data Accesses
|
||||||
|
|
||||||
|
The C11 memory model attempts to bridge the gap by allowing us to talk about the
|
||||||
|
*causality* of our program. Generally, this is by establishing a *happens
|
||||||
|
before* relationship between parts of the program and the threads that are
|
||||||
|
running them. This gives the hardware and compiler room to optimize the program
|
||||||
|
more aggressively where a strict happens-before relationship isn't established,
|
||||||
|
but forces them to be more careful where one is established. The way we
|
||||||
|
communicate these relationships are through *data accesses* and *atomic
|
||||||
|
accesses*.
|
||||||
|
|
||||||
|
Data accesses are the bread-and-butter of the programming world. They are
|
||||||
|
fundamentally unsynchronized and compilers are free to aggressively optimize
|
||||||
|
them. In particular, data accesses are free to be reordered by the compiler on
|
||||||
|
the assumption that the program is single-threaded. The hardware is also free to
|
||||||
|
propagate the changes made in data accesses to other threads as lazily and
|
||||||
|
inconsistently as it wants. Mostly critically, data accesses are how data races
|
||||||
|
happen. Data accesses are very friendly to the hardware and compiler, but as
|
||||||
|
we've seen they offer *awful* semantics to try to write synchronized code with.
|
||||||
|
Actually, that's too weak.
|
||||||
|
|
||||||
|
**It is literally impossible to write correct synchronized code using only data
|
||||||
|
accesses.**
|
||||||
|
|
||||||
|
Atomic accesses are how we tell the hardware and compiler that our program is
|
||||||
|
multi-threaded. Each atomic access can be marked with an *ordering* that
|
||||||
|
specifies what kind of relationship it establishes with other accesses. In
|
||||||
|
practice, this boils down to telling the compiler and hardware certain things
|
||||||
|
they *can't* do. For the compiler, this largely revolves around re-ordering of
|
||||||
|
instructions. For the hardware, this largely revolves around how writes are
|
||||||
|
propagated to other threads. The set of orderings Rust exposes are:
|
||||||
|
|
||||||
|
* Sequentially Consistent (SeqCst)
|
||||||
|
* Release
|
||||||
|
* Acquire
|
||||||
|
* Relaxed
|
||||||
|
|
||||||
|
(Note: We explicitly do not expose the C11 *consume* ordering)
|
||||||
|
|
||||||
|
TODO: negative reasoning vs positive reasoning? TODO: "can't forget to
|
||||||
|
synchronize"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Sequentially Consistent
|
||||||
|
|
||||||
|
Sequentially Consistent is the most powerful of all, implying the restrictions
|
||||||
|
of all other orderings. Intuitively, a sequentially consistent operation
|
||||||
|
cannot be reordered: all accesses on one thread that happen before and after a
|
||||||
|
SeqCst access stay before and after it. A data-race-free program that uses
|
||||||
|
only sequentially consistent atomics and data accesses has the very nice
|
||||||
|
property that there is a single global execution of the program's instructions
|
||||||
|
that all threads agree on. This execution is also particularly nice to reason
|
||||||
|
about: it's just an interleaving of each thread's individual executions. This
|
||||||
|
does not hold if you start using the weaker atomic orderings.
|
||||||
|
|
||||||
|
The relative developer-friendliness of sequential consistency doesn't come for
|
||||||
|
free. Even on strongly-ordered platforms sequential consistency involves
|
||||||
|
emitting memory fences.
|
||||||
|
|
||||||
|
In practice, sequential consistency is rarely necessary for program correctness.
|
||||||
|
However sequential consistency is definitely the right choice if you're not
|
||||||
|
confident about the other memory orders. Having your program run a bit slower
|
||||||
|
than it needs to is certainly better than it running incorrectly! It's also
|
||||||
|
mechanically trivial to downgrade atomic operations to have a weaker
|
||||||
|
consistency later on. Just change `SeqCst` to `Relaxed` and you're done! Of
|
||||||
|
course, proving that this transformation is *correct* is a whole other matter.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Acquire-Release
|
||||||
|
|
||||||
|
Acquire and Release are largely intended to be paired. Their names hint at their
|
||||||
|
use case: they're perfectly suited for acquiring and releasing locks, and
|
||||||
|
ensuring that critical sections don't overlap.
|
||||||
|
|
||||||
|
Intuitively, an acquire access ensures that every access after it stays after
|
||||||
|
it. However operations that occur before an acquire are free to be reordered to
|
||||||
|
occur after it. Similarly, a release access ensures that every access before it
|
||||||
|
stays before it. However operations that occur after a release are free to be
|
||||||
|
reordered to occur before it.
|
||||||
|
|
||||||
|
When thread A releases a location in memory and then thread B subsequently
|
||||||
|
acquires *the same* location in memory, causality is established. Every write
|
||||||
|
that happened before A's release will be observed by B after its release.
|
||||||
|
However no causality is established with any other threads. Similarly, no
|
||||||
|
causality is established if A and B access *different* locations in memory.
|
||||||
|
|
||||||
|
Basic use of release-acquire is therefore simple: you acquire a location of
|
||||||
|
memory to begin the critical section, and then release that location to end it.
|
||||||
|
For instance, a simple spinlock might look like:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
use std::thread;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let lock = Arc::new(AtomicBool::new(true)); // value answers "am I locked?"
|
||||||
|
|
||||||
|
// ... distribute lock to threads somehow ...
|
||||||
|
|
||||||
|
// Try to acquire the lock by setting it to false
|
||||||
|
while !lock.compare_and_swap(true, false, Ordering::Acquire) { }
|
||||||
|
// broke out of the loop, so we successfully acquired the lock!
|
||||||
|
|
||||||
|
// ... scary data accesses ...
|
||||||
|
|
||||||
|
// ok we're done, release the lock
|
||||||
|
lock.store(true, Ordering::Release);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
On strongly-ordered platforms most accesses have release or acquire semantics,
|
||||||
|
making release and acquire often totally free. This is not the case on
|
||||||
|
weakly-ordered platforms.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Relaxed
|
||||||
|
|
||||||
|
Relaxed accesses are the absolute weakest. They can be freely re-ordered and
|
||||||
|
provide no happens-before relationship. Still, relaxed operations are still
|
||||||
|
atomic. That is, they don't count as data accesses and any read-modify-write
|
||||||
|
operations done to them occur atomically. Relaxed operations are appropriate for
|
||||||
|
things that you definitely want to happen, but don't particularly otherwise care
|
||||||
|
about. For instance, incrementing a counter can be safely done by multiple
|
||||||
|
threads using a relaxed `fetch_add` if you're not using the counter to
|
||||||
|
synchronize any other accesses.
|
||||||
|
|
||||||
|
There's rarely a benefit in making an operation relaxed on strongly-ordered
|
||||||
|
platforms, since they usually provide release-acquire semantics anyway. However
|
||||||
|
relaxed operations can be cheaper on weakly-ordered platforms.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[C11-busted]: http://plv.mpi-sws.org/c11comp/popl15.pdf
|
||||||
|
[C11-model]: http://www.open-std.org/jtc1/sc22/wg14/www/standards.html#9899
|
291
src/doc/nomicon/borrow-splitting.md
Normal file
291
src/doc/nomicon/borrow-splitting.md
Normal file
@ -0,0 +1,291 @@
|
|||||||
|
% Splitting Borrows
|
||||||
|
|
||||||
|
The mutual exclusion property of mutable references can be very limiting when
|
||||||
|
working with a composite structure. The borrow checker understands some basic
|
||||||
|
stuff, but will fall over pretty easily. It does understand structs
|
||||||
|
sufficiently to know that it's possible to borrow disjoint fields of a struct
|
||||||
|
simultaneously. So this works today:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
struct Foo {
|
||||||
|
a: i32,
|
||||||
|
b: i32,
|
||||||
|
c: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut x = Foo {a: 0, b: 0, c: 0};
|
||||||
|
let a = &mut x.a;
|
||||||
|
let b = &mut x.b;
|
||||||
|
let c = &x.c;
|
||||||
|
*b += 1;
|
||||||
|
let c2 = &x.c;
|
||||||
|
*a += 10;
|
||||||
|
println!("{} {} {} {}", a, b, c, c2);
|
||||||
|
```
|
||||||
|
|
||||||
|
However borrowck doesn't understand arrays or slices in any way, so this doesn't
|
||||||
|
work:
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
let mut x = [1, 2, 3];
|
||||||
|
let a = &mut x[0];
|
||||||
|
let b = &mut x[1];
|
||||||
|
println!("{} {}", a, b);
|
||||||
|
```
|
||||||
|
|
||||||
|
```text
|
||||||
|
<anon>:4:14: 4:18 error: cannot borrow `x[..]` as mutable more than once at a time
|
||||||
|
<anon>:4 let b = &mut x[1];
|
||||||
|
^~~~
|
||||||
|
<anon>:3:14: 3:18 note: previous borrow of `x[..]` occurs here; the mutable borrow prevents subsequent moves, borrows, or modification of `x[..]` until the borrow ends
|
||||||
|
<anon>:3 let a = &mut x[0];
|
||||||
|
^~~~
|
||||||
|
<anon>:6:2: 6:2 note: previous borrow ends here
|
||||||
|
<anon>:1 fn main() {
|
||||||
|
<anon>:2 let mut x = [1, 2, 3];
|
||||||
|
<anon>:3 let a = &mut x[0];
|
||||||
|
<anon>:4 let b = &mut x[1];
|
||||||
|
<anon>:5 println!("{} {}", a, b);
|
||||||
|
<anon>:6 }
|
||||||
|
^
|
||||||
|
error: aborting due to 2 previous errors
|
||||||
|
```
|
||||||
|
|
||||||
|
While it was plausible that borrowck could understand this simple case, it's
|
||||||
|
pretty clearly hopeless for borrowck to understand disjointness in general
|
||||||
|
container types like a tree, especially if distinct keys actually *do* map
|
||||||
|
to the same value.
|
||||||
|
|
||||||
|
In order to "teach" borrowck that what we're doing is ok, we need to drop down
|
||||||
|
to unsafe code. For instance, mutable slices expose a `split_at_mut` function
|
||||||
|
that consumes the slice and returns two mutable slices. One for everything to
|
||||||
|
the left of the index, and one for everything to the right. Intuitively we know
|
||||||
|
this is safe because the slices don't overlap, and therefore alias. However
|
||||||
|
the implementation requires some unsafety:
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
fn split_at_mut(&mut self, mid: usize) -> (&mut [T], &mut [T]) {
|
||||||
|
let len = self.len();
|
||||||
|
let ptr = self.as_mut_ptr();
|
||||||
|
assert!(mid <= len);
|
||||||
|
unsafe {
|
||||||
|
(from_raw_parts_mut(ptr, mid),
|
||||||
|
from_raw_parts_mut(ptr.offset(mid as isize), len - mid))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This is actually a bit subtle. So as to avoid ever making two `&mut`'s to the
|
||||||
|
same value, we explicitly construct brand-new slices through raw pointers.
|
||||||
|
|
||||||
|
However more subtle is how iterators that yield mutable references work.
|
||||||
|
The iterator trait is defined as follows:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
trait Iterator {
|
||||||
|
type Item;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item>;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Given this definition, Self::Item has *no* connection to `self`. This means that
|
||||||
|
we can call `next` several times in a row, and hold onto all the results
|
||||||
|
*concurrently*. This is perfectly fine for by-value iterators, which have
|
||||||
|
exactly these semantics. It's also actually fine for shared references, as they
|
||||||
|
admit arbitrarily many references to the same thing (although the iterator needs
|
||||||
|
to be a separate object from the thing being shared).
|
||||||
|
|
||||||
|
But mutable references make this a mess. At first glance, they might seem
|
||||||
|
completely incompatible with this API, as it would produce multiple mutable
|
||||||
|
references to the same object!
|
||||||
|
|
||||||
|
However it actually *does* work, exactly because iterators are one-shot objects.
|
||||||
|
Everything an IterMut yields will be yielded at most once, so we don't
|
||||||
|
actually ever yield multiple mutable references to the same piece of data.
|
||||||
|
|
||||||
|
Perhaps surprisingly, mutable iterators don't require unsafe code to be
|
||||||
|
implemented for many types!
|
||||||
|
|
||||||
|
For instance here's a singly linked list:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
# fn main() {}
|
||||||
|
type Link<T> = Option<Box<Node<T>>>;
|
||||||
|
|
||||||
|
struct Node<T> {
|
||||||
|
elem: T,
|
||||||
|
next: Link<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct LinkedList<T> {
|
||||||
|
head: Link<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct IterMut<'a, T: 'a>(Option<&'a mut Node<T>>);
|
||||||
|
|
||||||
|
impl<T> LinkedList<T> {
|
||||||
|
fn iter_mut(&mut self) -> IterMut<T> {
|
||||||
|
IterMut(self.head.as_mut().map(|node| &mut **node))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T> Iterator for IterMut<'a, T> {
|
||||||
|
type Item = &'a mut T;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
self.0.take().map(|node| {
|
||||||
|
self.0 = node.next.as_mut().map(|node| &mut **node);
|
||||||
|
&mut node.elem
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Here's a mutable slice:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
# fn main() {}
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
|
pub struct IterMut<'a, T: 'a>(&'a mut[T]);
|
||||||
|
|
||||||
|
impl<'a, T> Iterator for IterMut<'a, T> {
|
||||||
|
type Item = &'a mut T;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
let slice = mem::replace(&mut self.0, &mut []);
|
||||||
|
if slice.is_empty() { return None; }
|
||||||
|
|
||||||
|
let (l, r) = slice.split_at_mut(1);
|
||||||
|
self.0 = r;
|
||||||
|
l.get_mut(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T> DoubleEndedIterator for IterMut<'a, T> {
|
||||||
|
fn next_back(&mut self) -> Option<Self::Item> {
|
||||||
|
let slice = mem::replace(&mut self.0, &mut []);
|
||||||
|
if slice.is_empty() { return None; }
|
||||||
|
|
||||||
|
let new_len = slice.len() - 1;
|
||||||
|
let (l, r) = slice.split_at_mut(new_len);
|
||||||
|
self.0 = l;
|
||||||
|
r.get_mut(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
And here's a binary tree:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
# fn main() {}
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
|
type Link<T> = Option<Box<Node<T>>>;
|
||||||
|
|
||||||
|
struct Node<T> {
|
||||||
|
elem: T,
|
||||||
|
left: Link<T>,
|
||||||
|
right: Link<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Tree<T> {
|
||||||
|
root: Link<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct NodeIterMut<'a, T: 'a> {
|
||||||
|
elem: Option<&'a mut T>,
|
||||||
|
left: Option<&'a mut Node<T>>,
|
||||||
|
right: Option<&'a mut Node<T>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum State<'a, T: 'a> {
|
||||||
|
Elem(&'a mut T),
|
||||||
|
Node(&'a mut Node<T>),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct IterMut<'a, T: 'a>(VecDeque<NodeIterMut<'a, T>>);
|
||||||
|
|
||||||
|
impl<T> Tree<T> {
|
||||||
|
pub fn iter_mut(&mut self) -> IterMut<T> {
|
||||||
|
let mut deque = VecDeque::new();
|
||||||
|
self.root.as_mut().map(|root| deque.push_front(root.iter_mut()));
|
||||||
|
IterMut(deque)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Node<T> {
|
||||||
|
pub fn iter_mut(&mut self) -> NodeIterMut<T> {
|
||||||
|
NodeIterMut {
|
||||||
|
elem: Some(&mut self.elem),
|
||||||
|
left: self.left.as_mut().map(|node| &mut **node),
|
||||||
|
right: self.right.as_mut().map(|node| &mut **node),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl<'a, T> Iterator for NodeIterMut<'a, T> {
|
||||||
|
type Item = State<'a, T>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
match self.left.take() {
|
||||||
|
Some(node) => Some(State::Node(node)),
|
||||||
|
None => match self.elem.take() {
|
||||||
|
Some(elem) => Some(State::Elem(elem)),
|
||||||
|
None => match self.right.take() {
|
||||||
|
Some(node) => Some(State::Node(node)),
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T> DoubleEndedIterator for NodeIterMut<'a, T> {
|
||||||
|
fn next_back(&mut self) -> Option<Self::Item> {
|
||||||
|
match self.right.take() {
|
||||||
|
Some(node) => Some(State::Node(node)),
|
||||||
|
None => match self.elem.take() {
|
||||||
|
Some(elem) => Some(State::Elem(elem)),
|
||||||
|
None => match self.left.take() {
|
||||||
|
Some(node) => Some(State::Node(node)),
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T> Iterator for IterMut<'a, T> {
|
||||||
|
type Item = &'a mut T;
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
loop {
|
||||||
|
match self.0.front_mut().and_then(|node_it| node_it.next()) {
|
||||||
|
Some(State::Elem(elem)) => return Some(elem),
|
||||||
|
Some(State::Node(node)) => self.0.push_front(node.iter_mut()),
|
||||||
|
None => if let None = self.0.pop_front() { return None },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T> DoubleEndedIterator for IterMut<'a, T> {
|
||||||
|
fn next_back(&mut self) -> Option<Self::Item> {
|
||||||
|
loop {
|
||||||
|
match self.0.back_mut().and_then(|node_it| node_it.next_back()) {
|
||||||
|
Some(State::Elem(elem)) => return Some(elem),
|
||||||
|
Some(State::Node(node)) => self.0.push_back(node.iter_mut()),
|
||||||
|
None => if let None = self.0.pop_back() { return None },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
All of these are completely safe and work on stable Rust! This ultimately
|
||||||
|
falls out of the simple struct case we saw before: Rust understands that you
|
||||||
|
can safely split a mutable reference into subfields. We can then encode
|
||||||
|
permanently consuming a reference via Options (or in the case of slices,
|
||||||
|
replacing with an empty slice).
|
71
src/doc/nomicon/casts.md
Normal file
71
src/doc/nomicon/casts.md
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
% Casts
|
||||||
|
|
||||||
|
Casts are a superset of coercions: every coercion can be explicitly
|
||||||
|
invoked via a cast. However some conversions require a cast.
|
||||||
|
While coercions are pervasive and largely harmless, these "true casts"
|
||||||
|
are rare and potentially dangerous. As such, casts must be explicitly invoked
|
||||||
|
using the `as` keyword: `expr as Type`.
|
||||||
|
|
||||||
|
True casts generally revolve around raw pointers and the primitive numeric
|
||||||
|
types. Even though they're dangerous, these casts are infallible at runtime.
|
||||||
|
If a cast triggers some subtle corner case no indication will be given that
|
||||||
|
this occurred. The cast will simply succeed. That said, casts must be valid
|
||||||
|
at the type level, or else they will be prevented statically. For instance,
|
||||||
|
`7u8 as bool` will not compile.
|
||||||
|
|
||||||
|
That said, casts aren't `unsafe` because they generally can't violate memory
|
||||||
|
safety *on their own*. For instance, converting an integer to a raw pointer can
|
||||||
|
very easily lead to terrible things. However the act of creating the pointer
|
||||||
|
itself is safe, because actually using a raw pointer is already marked as
|
||||||
|
`unsafe`.
|
||||||
|
|
||||||
|
Here's an exhaustive list of all the true casts. For brevity, we will use `*`
|
||||||
|
to denote either a `*const` or `*mut`, and `integer` to denote any integral
|
||||||
|
primitive:
|
||||||
|
|
||||||
|
* `*T as *U` where `T, U: Sized`
|
||||||
|
* `*T as *U` TODO: explain unsized situation
|
||||||
|
* `*T as integer`
|
||||||
|
* `integer as *T`
|
||||||
|
* `number as number`
|
||||||
|
* `C-like-enum as integer`
|
||||||
|
* `bool as integer`
|
||||||
|
* `char as integer`
|
||||||
|
* `u8 as char`
|
||||||
|
* `&[T; n] as *const T`
|
||||||
|
* `fn as *T` where `T: Sized`
|
||||||
|
* `fn as integer`
|
||||||
|
|
||||||
|
Note that lengths are not adjusted when casting raw slices -
|
||||||
|
`*const [u16] as *const [u8]` creates a slice that only includes
|
||||||
|
half of the original memory.
|
||||||
|
|
||||||
|
Casting is not transitive, that is, even if `e as U1 as U2` is a valid
|
||||||
|
expression, `e as U2` is not necessarily so.
|
||||||
|
|
||||||
|
For numeric casts, there are quite a few cases to consider:
|
||||||
|
|
||||||
|
* casting between two integers of the same size (e.g. i32 -> u32) is a no-op
|
||||||
|
* casting from a larger integer to a smaller integer (e.g. u32 -> u8) will
|
||||||
|
truncate
|
||||||
|
* casting from a smaller integer to a larger integer (e.g. u8 -> u32) will
|
||||||
|
* zero-extend if the source is unsigned
|
||||||
|
* sign-extend if the source is signed
|
||||||
|
* casting from a float to an integer will round the float towards zero
|
||||||
|
* **[NOTE: currently this will cause Undefined Behaviour if the rounded
|
||||||
|
value cannot be represented by the target integer type][float-int]**.
|
||||||
|
This includes Inf and NaN. This is a bug and will be fixed.
|
||||||
|
* casting from an integer to float will produce the floating point
|
||||||
|
representation of the integer, rounded if necessary (rounding strategy
|
||||||
|
unspecified)
|
||||||
|
* casting from an f32 to an f64 is perfect and lossless
|
||||||
|
* casting from an f64 to an f32 will produce the closest possible value
|
||||||
|
(rounding strategy unspecified)
|
||||||
|
* **[NOTE: currently this will cause Undefined Behaviour if the value
|
||||||
|
is finite but larger or smaller than the largest or smallest finite
|
||||||
|
value representable by f32][float-float]**. This is a bug and will
|
||||||
|
be fixed.
|
||||||
|
|
||||||
|
|
||||||
|
[float-int]: https://github.com/rust-lang/rust/issues/10184
|
||||||
|
[float-float]: https://github.com/rust-lang/rust/issues/15536
|
117
src/doc/nomicon/checked-uninit.md
Normal file
117
src/doc/nomicon/checked-uninit.md
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
% Checked Uninitialized Memory
|
||||||
|
|
||||||
|
Like C, all stack variables in Rust are uninitialized until a value is
|
||||||
|
explicitly assigned to them. Unlike C, Rust statically prevents you from ever
|
||||||
|
reading them until you do:
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
fn main() {
|
||||||
|
let x: i32;
|
||||||
|
println!("{}", x);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```text
|
||||||
|
src/main.rs:3:20: 3:21 error: use of possibly uninitialized variable: `x`
|
||||||
|
src/main.rs:3 println!("{}", x);
|
||||||
|
^
|
||||||
|
```
|
||||||
|
|
||||||
|
This is based off of a basic branch analysis: every branch must assign a value
|
||||||
|
to `x` before it is first used. Interestingly, Rust doesn't require the variable
|
||||||
|
to be mutable to perform a delayed initialization if every branch assigns
|
||||||
|
exactly once. However the analysis does not take advantage of constant analysis
|
||||||
|
or anything like that. So this compiles:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn main() {
|
||||||
|
let x: i32;
|
||||||
|
|
||||||
|
if true {
|
||||||
|
x = 1;
|
||||||
|
} else {
|
||||||
|
x = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("{}", x);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
but this doesn't:
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
fn main() {
|
||||||
|
let x: i32;
|
||||||
|
if true {
|
||||||
|
x = 1;
|
||||||
|
}
|
||||||
|
println!("{}", x);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```text
|
||||||
|
src/main.rs:6:17: 6:18 error: use of possibly uninitialized variable: `x`
|
||||||
|
src/main.rs:6 println!("{}", x);
|
||||||
|
```
|
||||||
|
|
||||||
|
while this does:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn main() {
|
||||||
|
let x: i32;
|
||||||
|
if true {
|
||||||
|
x = 1;
|
||||||
|
println!("{}", x);
|
||||||
|
}
|
||||||
|
// Don't care that there are branches where it's not initialized
|
||||||
|
// since we don't use the value in those branches
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Of course, while the analysis doesn't consider actual values, it does
|
||||||
|
have a relatively sophisticated understanding of dependencies and control
|
||||||
|
flow. For instance, this works:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let x: i32;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
// Rust doesn't understand that this branch will be taken unconditionally,
|
||||||
|
// because it relies on actual values.
|
||||||
|
if true {
|
||||||
|
// But it does understand that it will only be taken once because
|
||||||
|
// we unconditionally break out of it. Therefore `x` doesn't
|
||||||
|
// need to be marked as mutable.
|
||||||
|
x = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// It also knows that it's impossible to get here without reaching the break.
|
||||||
|
// And therefore that `x` must be initialized here!
|
||||||
|
println!("{}", x);
|
||||||
|
```
|
||||||
|
|
||||||
|
If a value is moved out of a variable, that variable becomes logically
|
||||||
|
uninitialized if the type of the value isn't Copy. That is:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn main() {
|
||||||
|
let x = 0;
|
||||||
|
let y = Box::new(0);
|
||||||
|
let z1 = x; // x is still valid because i32 is Copy
|
||||||
|
let z2 = y; // y is now logically uninitialized because Box isn't Copy
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
However reassigning `y` in this example *would* require `y` to be marked as
|
||||||
|
mutable, as a Safe Rust program could observe that the value of `y` changed:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn main() {
|
||||||
|
let mut y = Box::new(0);
|
||||||
|
let z = y; // y is now logically uninitialized because Box isn't Copy
|
||||||
|
y = Box::new(1); // reinitialize y
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Otherwise it's like `y` is a brand new variable.
|
70
src/doc/nomicon/coercions.md
Normal file
70
src/doc/nomicon/coercions.md
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
% Coercions
|
||||||
|
|
||||||
|
Types can implicitly be coerced to change in certain contexts. These changes are
|
||||||
|
generally just *weakening* of types, largely focused around pointers and
|
||||||
|
lifetimes. They mostly exist to make Rust "just work" in more cases, and are
|
||||||
|
largely harmless.
|
||||||
|
|
||||||
|
Here's all the kinds of coercion:
|
||||||
|
|
||||||
|
Coercion is allowed between the following types:
|
||||||
|
|
||||||
|
* Transitivity: `T_1` to `T_3` where `T_1` coerces to `T_2` and `T_2` coerces to
|
||||||
|
`T_3`
|
||||||
|
* Pointer Weakening:
|
||||||
|
* `&mut T` to `&T`
|
||||||
|
* `*mut T` to `*const T`
|
||||||
|
* `&T` to `*const T`
|
||||||
|
* `&mut T` to `*mut T`
|
||||||
|
* Unsizing: `T` to `U` if `T` implements `CoerceUnsized<U>`
|
||||||
|
|
||||||
|
`CoerceUnsized<Pointer<U>> for Pointer<T> where T: Unsize<U>` is implemented
|
||||||
|
for all pointer types (including smart pointers like Box and Rc). Unsize is
|
||||||
|
only implemented automatically, and enables the following transformations:
|
||||||
|
|
||||||
|
* `[T, ..n]` => `[T]`
|
||||||
|
* `T` => `Trait` where `T: Trait`
|
||||||
|
* `Foo<..., T, ...>` => `Foo<..., U, ...>` where:
|
||||||
|
* `T: Unsize<U>`
|
||||||
|
* `Foo` is a struct
|
||||||
|
* Only the last field of `Foo` has type `T`
|
||||||
|
* `T` is not part of the type of any other fields
|
||||||
|
|
||||||
|
Coercions occur at a *coercion site*. Any location that is explicitly typed
|
||||||
|
will cause a coercion to its type. If inference is necessary, the coercion will
|
||||||
|
not be performed. Exhaustively, the coercion sites for an expression `e` to
|
||||||
|
type `U` are:
|
||||||
|
|
||||||
|
* let statements, statics, and consts: `let x: U = e`
|
||||||
|
* Arguments to functions: `takes_a_U(e)`
|
||||||
|
* Any expression that will be returned: `fn foo() -> U { e }`
|
||||||
|
* Struct literals: `Foo { some_u: e }`
|
||||||
|
* Array literals: `let x: [U; 10] = [e, ..]`
|
||||||
|
* Tuple literals: `let x: (U, ..) = (e, ..)`
|
||||||
|
* The last expression in a block: `let x: U = { ..; e }`
|
||||||
|
|
||||||
|
Note that we do not perform coercions when matching traits (except for
|
||||||
|
receivers, see below). If there is an impl for some type `U` and `T` coerces to
|
||||||
|
`U`, that does not constitute an implementation for `T`. For example, the
|
||||||
|
following will not type check, even though it is OK to coerce `t` to `&T` and
|
||||||
|
there is an impl for `&T`:
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
trait Trait {}
|
||||||
|
|
||||||
|
fn foo<X: Trait>(t: X) {}
|
||||||
|
|
||||||
|
impl<'a> Trait for &'a i32 {}
|
||||||
|
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let t: &mut i32 = &mut 0;
|
||||||
|
foo(t);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```text
|
||||||
|
<anon>:10:5: 10:8 error: the trait `Trait` is not implemented for the type `&mut i32` [E0277]
|
||||||
|
<anon>:10 foo(t);
|
||||||
|
^~~
|
||||||
|
```
|
13
src/doc/nomicon/concurrency.md
Normal file
13
src/doc/nomicon/concurrency.md
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
% Concurrency and Paralellism
|
||||||
|
|
||||||
|
Rust as a language doesn't *really* have an opinion on how to do concurrency or
|
||||||
|
parallelism. The standard library exposes OS threads and blocking sys-calls
|
||||||
|
because everyone has those, and they're uniform enough that you can provide
|
||||||
|
an abstraction over them in a relatively uncontroversial way. Message passing,
|
||||||
|
green threads, and async APIs are all diverse enough that any abstraction over
|
||||||
|
them tends to involve trade-offs that we weren't willing to commit to for 1.0.
|
||||||
|
|
||||||
|
However the way Rust models concurrency makes it relatively easy design your own
|
||||||
|
concurrency paradigm as a library and have everyone else's code Just Work
|
||||||
|
with yours. Just require the right lifetimes and Send and Sync where appropriate
|
||||||
|
and you're off to the races. Or rather, off to the... not... having... races.
|
59
src/doc/nomicon/constructors.md
Normal file
59
src/doc/nomicon/constructors.md
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
% Constructors
|
||||||
|
|
||||||
|
There is exactly one way to create an instance of a user-defined type: name it,
|
||||||
|
and initialize all its fields at once:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
struct Foo {
|
||||||
|
a: u8,
|
||||||
|
b: u32,
|
||||||
|
c: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Bar {
|
||||||
|
X(u32),
|
||||||
|
Y(bool),
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Unit;
|
||||||
|
|
||||||
|
let foo = Foo { a: 0, b: 1, c: false };
|
||||||
|
let bar = Bar::X(0);
|
||||||
|
let empty = Unit;
|
||||||
|
```
|
||||||
|
|
||||||
|
That's it. Every other way you make an instance of a type is just calling a
|
||||||
|
totally vanilla function that does some stuff and eventually bottoms out to The
|
||||||
|
One True Constructor.
|
||||||
|
|
||||||
|
Unlike C++, Rust does not come with a slew of built-in kinds of constructor.
|
||||||
|
There are no Copy, Default, Assignment, Move, or whatever constructors. The
|
||||||
|
reasons for this are varied, but it largely boils down to Rust's philosophy of
|
||||||
|
*being explicit*.
|
||||||
|
|
||||||
|
Move constructors are meaningless in Rust because we don't enable types to
|
||||||
|
"care" about their location in memory. Every type must be ready for it to be
|
||||||
|
blindly memcopied to somewhere else in memory. This means pure on-the-stack-but-
|
||||||
|
still-movable intrusive linked lists are simply not happening in Rust (safely).
|
||||||
|
|
||||||
|
Assignment and copy constructors similarly don't exist because move semantics
|
||||||
|
are the only semantics in Rust. At most `x = y` just moves the bits of y into
|
||||||
|
the x variable. Rust does provide two facilities for providing C++'s copy-
|
||||||
|
oriented semantics: `Copy` and `Clone`. Clone is our moral equivalent of a copy
|
||||||
|
constructor, but it's never implicitly invoked. You have to explicitly call
|
||||||
|
`clone` on an element you want to be cloned. Copy is a special case of Clone
|
||||||
|
where the implementation is just "copy the bits". Copy types *are* implicitly
|
||||||
|
cloned whenever they're moved, but because of the definition of Copy this just
|
||||||
|
means not treating the old copy as uninitialized -- a no-op.
|
||||||
|
|
||||||
|
While Rust provides a `Default` trait for specifying the moral equivalent of a
|
||||||
|
default constructor, it's incredibly rare for this trait to be used. This is
|
||||||
|
because variables [aren't implicitly initialized][uninit]. Default is basically
|
||||||
|
only useful for generic programming. In concrete contexts, a type will provide a
|
||||||
|
static `new` method for any kind of "default" constructor. This has no relation
|
||||||
|
to `new` in other languages and has no special meaning. It's just a naming
|
||||||
|
convention.
|
||||||
|
|
||||||
|
TODO: talk about "placement new"?
|
||||||
|
|
||||||
|
[uninit]: uninitialized.html
|
34
src/doc/nomicon/conversions.md
Normal file
34
src/doc/nomicon/conversions.md
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
% Type Conversions
|
||||||
|
|
||||||
|
At the end of the day, everything is just a pile of bits somewhere, and type
|
||||||
|
systems are just there to help us use those bits right. There are two common
|
||||||
|
problems with typing bits: needing to reinterpret those exact bits as a
|
||||||
|
different type, and needing to change the bits to have equivalent meaning for
|
||||||
|
a different type. Because Rust encourages encoding important properties in the
|
||||||
|
type system, these problems are incredibly pervasive. As such, Rust
|
||||||
|
consequently gives you several ways to solve them.
|
||||||
|
|
||||||
|
First we'll look at the ways that Safe Rust gives you to reinterpret values.
|
||||||
|
The most trivial way to do this is to just destructure a value into its
|
||||||
|
constituent parts and then build a new type out of them. e.g.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
struct Foo {
|
||||||
|
x: u32,
|
||||||
|
y: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Bar {
|
||||||
|
a: u32,
|
||||||
|
b: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reinterpret(foo: Foo) -> Bar {
|
||||||
|
let Foo { x, y } = foo;
|
||||||
|
Bar { a: x, b: y }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
But this is, at best, annoying. For common conversions, Rust provides
|
||||||
|
more ergonomic alternatives.
|
||||||
|
|
5
src/doc/nomicon/data.md
Normal file
5
src/doc/nomicon/data.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
% Data Representation in Rust
|
||||||
|
|
||||||
|
Low-level programming cares a lot about data layout. It's a big deal. It also
|
||||||
|
pervasively influences the rest of the language, so we're going to start by
|
||||||
|
digging into how data is represented in Rust.
|
181
src/doc/nomicon/destructors.md
Normal file
181
src/doc/nomicon/destructors.md
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
% Destructors
|
||||||
|
|
||||||
|
What the language *does* provide is full-blown automatic destructors through the
|
||||||
|
`Drop` trait, which provides the following method:
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
fn drop(&mut self);
|
||||||
|
```
|
||||||
|
|
||||||
|
This method gives the type time to somehow finish what it was doing.
|
||||||
|
|
||||||
|
**After `drop` is run, Rust will recursively try to drop all of the fields
|
||||||
|
of `self`.**
|
||||||
|
|
||||||
|
This is a convenience feature so that you don't have to write "destructor
|
||||||
|
boilerplate" to drop children. If a struct has no special logic for being
|
||||||
|
dropped other than dropping its children, then it means `Drop` doesn't need to
|
||||||
|
be implemented at all!
|
||||||
|
|
||||||
|
**There is no stable way to prevent this behaviour in Rust 1.0.**
|
||||||
|
|
||||||
|
Note that taking `&mut self` means that even if you could suppress recursive
|
||||||
|
Drop, Rust will prevent you from e.g. moving fields out of self. For most types,
|
||||||
|
this is totally fine.
|
||||||
|
|
||||||
|
For instance, a custom implementation of `Box` might write `Drop` like this:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
#![feature(heap_api, core_intrinsics, unique)]
|
||||||
|
|
||||||
|
use std::rt::heap;
|
||||||
|
use std::ptr::Unique;
|
||||||
|
use std::intrinsics::drop_in_place;
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
|
struct Box<T>{ ptr: Unique<T> }
|
||||||
|
|
||||||
|
impl<T> Drop for Box<T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
drop_in_place(*self.ptr);
|
||||||
|
heap::deallocate((*self.ptr) as *mut u8,
|
||||||
|
mem::size_of::<T>(),
|
||||||
|
mem::align_of::<T>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
and this works fine because when Rust goes to drop the `ptr` field it just sees
|
||||||
|
a [Unique][] that has no actual `Drop` implementation. Similarly nothing can
|
||||||
|
use-after-free the `ptr` because when drop exits, it becomes inacessible.
|
||||||
|
|
||||||
|
However this wouldn't work:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
#![feature(heap_api, core_intrinsics, unique)]
|
||||||
|
|
||||||
|
use std::rt::heap;
|
||||||
|
use std::ptr::Unique;
|
||||||
|
use std::intrinsics::drop_in_place;
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
|
struct Box<T>{ ptr: Unique<T> }
|
||||||
|
|
||||||
|
impl<T> Drop for Box<T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
drop_in_place(*self.ptr);
|
||||||
|
heap::deallocate((*self.ptr) as *mut u8,
|
||||||
|
mem::size_of::<T>(),
|
||||||
|
mem::align_of::<T>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SuperBox<T> { my_box: Box<T> }
|
||||||
|
|
||||||
|
impl<T> Drop for SuperBox<T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
// Hyper-optimized: deallocate the box's contents for it
|
||||||
|
// without `drop`ing the contents
|
||||||
|
heap::deallocate((*self.my_box.ptr) as *mut u8,
|
||||||
|
mem::size_of::<T>(),
|
||||||
|
mem::align_of::<T>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
After we deallocate the `box`'s ptr in SuperBox's destructor, Rust will
|
||||||
|
happily proceed to tell the box to Drop itself and everything will blow up with
|
||||||
|
use-after-frees and double-frees.
|
||||||
|
|
||||||
|
Note that the recursive drop behaviour applies to all structs and enums
|
||||||
|
regardless of whether they implement Drop. Therefore something like
|
||||||
|
|
||||||
|
```rust
|
||||||
|
struct Boxy<T> {
|
||||||
|
data1: Box<T>,
|
||||||
|
data2: Box<T>,
|
||||||
|
info: u32,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
will have its data1 and data2's fields destructors whenever it "would" be
|
||||||
|
dropped, even though it itself doesn't implement Drop. We say that such a type
|
||||||
|
*needs Drop*, even though it is not itself Drop.
|
||||||
|
|
||||||
|
Similarly,
|
||||||
|
|
||||||
|
```rust
|
||||||
|
enum Link {
|
||||||
|
Next(Box<Link>),
|
||||||
|
None,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
will have its inner Box field dropped if and only if an instance stores the
|
||||||
|
Next variant.
|
||||||
|
|
||||||
|
In general this works really nice because you don't need to worry about
|
||||||
|
adding/removing drops when you refactor your data layout. Still there's
|
||||||
|
certainly many valid usecases for needing to do trickier things with
|
||||||
|
destructors.
|
||||||
|
|
||||||
|
The classic safe solution to overriding recursive drop and allowing moving out
|
||||||
|
of Self during `drop` is to use an Option:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
#![feature(heap_api, core_intrinsics, unique)]
|
||||||
|
|
||||||
|
use std::rt::heap;
|
||||||
|
use std::ptr::Unique;
|
||||||
|
use std::intrinsics::drop_in_place;
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
|
struct Box<T>{ ptr: Unique<T> }
|
||||||
|
|
||||||
|
impl<T> Drop for Box<T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
drop_in_place(*self.ptr);
|
||||||
|
heap::deallocate((*self.ptr) as *mut u8,
|
||||||
|
mem::size_of::<T>(),
|
||||||
|
mem::align_of::<T>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SuperBox<T> { my_box: Option<Box<T>> }
|
||||||
|
|
||||||
|
impl<T> Drop for SuperBox<T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
// Hyper-optimized: deallocate the box's contents for it
|
||||||
|
// without `drop`ing the contents. Need to set the `box`
|
||||||
|
// field as `None` to prevent Rust from trying to Drop it.
|
||||||
|
let my_box = self.my_box.take().unwrap();
|
||||||
|
heap::deallocate((*my_box.ptr) as *mut u8,
|
||||||
|
mem::size_of::<T>(),
|
||||||
|
mem::align_of::<T>());
|
||||||
|
mem::forget(my_box);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
However this has fairly odd semantics: you're saying that a field that *should*
|
||||||
|
always be Some *may* be None, just because that happens in the destructor. Of
|
||||||
|
course this conversely makes a lot of sense: you can call arbitrary methods on
|
||||||
|
self during the destructor, and this should prevent you from ever doing so after
|
||||||
|
deinitializing the field. Not that it will prevent you from producing any other
|
||||||
|
arbitrarily invalid state in there.
|
||||||
|
|
||||||
|
On balance this is an ok choice. Certainly what you should reach for by default.
|
||||||
|
However, in the future we expect there to be a first-class way to announce that
|
||||||
|
a field shouldn't be automatically dropped.
|
||||||
|
|
||||||
|
[Unique]: phantom-data.html
|
6
src/doc/nomicon/dot-operator.md
Normal file
6
src/doc/nomicon/dot-operator.md
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
% The Dot Operator
|
||||||
|
|
||||||
|
The dot operator will perform a lot of magic to convert types. It will perform
|
||||||
|
auto-referencing, auto-dereferencing, and coercion until types match.
|
||||||
|
|
||||||
|
TODO: steal information from http://stackoverflow.com/questions/28519997/what-are-rusts-exact-auto-dereferencing-rules/28552082#28552082
|
95
src/doc/nomicon/drop-flags.md
Normal file
95
src/doc/nomicon/drop-flags.md
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
% Drop Flags
|
||||||
|
|
||||||
|
The examples in the previous section introduce an interesting problem for Rust.
|
||||||
|
We have seen that's possible to conditionally initialize, deinitialize, and
|
||||||
|
reinitialize locations of memory totally safely. For Copy types, this isn't
|
||||||
|
particularly notable since they're just a random pile of bits. However types
|
||||||
|
with destructors are a different story: Rust needs to know whether to call a
|
||||||
|
destructor whenever a variable is assigned to, or a variable goes out of scope.
|
||||||
|
How can it do this with conditional initialization?
|
||||||
|
|
||||||
|
Note that this is not a problem that all assignments need worry about. In
|
||||||
|
particular, assigning through a dereference unconditionally drops, and assigning
|
||||||
|
in a `let` unconditionally doesn't drop:
|
||||||
|
|
||||||
|
```
|
||||||
|
let mut x = Box::new(0); // let makes a fresh variable, so never need to drop
|
||||||
|
let y = &mut x;
|
||||||
|
*y = Box::new(1); // Deref assumes the referent is initialized, so always drops
|
||||||
|
```
|
||||||
|
|
||||||
|
This is only a problem when overwriting a previously initialized variable or
|
||||||
|
one of its subfields.
|
||||||
|
|
||||||
|
It turns out that Rust actually tracks whether a type should be dropped or not
|
||||||
|
*at runtime*. As a variable becomes initialized and uninitialized, a *drop flag*
|
||||||
|
for that variable is toggled. When a variable might need to be dropped, this
|
||||||
|
flag is evaluated to determine if it should be dropped.
|
||||||
|
|
||||||
|
Of course, it is often the case that a value's initialization state can be
|
||||||
|
statically known at every point in the program. If this is the case, then the
|
||||||
|
compiler can theoretically generate more efficient code! For instance, straight-
|
||||||
|
line code has such *static drop semantics*:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let mut x = Box::new(0); // x was uninit; just overwrite.
|
||||||
|
let mut y = x; // y was uninit; just overwrite and make x uninit.
|
||||||
|
x = Box::new(0); // x was uninit; just overwrite.
|
||||||
|
y = x; // y was init; Drop y, overwrite it, and make x uninit!
|
||||||
|
// y goes out of scope; y was init; Drop y!
|
||||||
|
// x goes out of scope; x was uninit; do nothing.
|
||||||
|
```
|
||||||
|
|
||||||
|
Similarly, branched code where all branches have the same behaviour with respect
|
||||||
|
to initialization has static drop semantics:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
# let condition = true;
|
||||||
|
let mut x = Box::new(0); // x was uninit; just overwrite.
|
||||||
|
if condition {
|
||||||
|
drop(x) // x gets moved out; make x uninit.
|
||||||
|
} else {
|
||||||
|
println!("{}", x);
|
||||||
|
drop(x) // x gets moved out; make x uninit.
|
||||||
|
}
|
||||||
|
x = Box::new(0); // x was uninit; just overwrite.
|
||||||
|
// x goes out of scope; x was init; Drop x!
|
||||||
|
```
|
||||||
|
|
||||||
|
However code like this *requires* runtime information to correctly Drop:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
# let condition = true;
|
||||||
|
let x;
|
||||||
|
if condition {
|
||||||
|
x = Box::new(0); // x was uninit; just overwrite.
|
||||||
|
println!("{}", x);
|
||||||
|
}
|
||||||
|
// x goes out of scope; x might be uninit;
|
||||||
|
// check the flag!
|
||||||
|
```
|
||||||
|
|
||||||
|
Of course, in this case it's trivial to retrieve static drop semantics:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
# let condition = true;
|
||||||
|
if condition {
|
||||||
|
let x = Box::new(0);
|
||||||
|
println!("{}", x);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
As of Rust 1.0, the drop flags are actually not-so-secretly stashed in a hidden
|
||||||
|
field of any type that implements Drop. Rust sets the drop flag by overwriting
|
||||||
|
the entire value with a particular bit pattern. This is pretty obviously Not
|
||||||
|
The Fastest and causes a bunch of trouble with optimizing code. It's legacy from
|
||||||
|
a time when you could do much more complex conditional initialization.
|
||||||
|
|
||||||
|
As such work is currently under way to move the flags out onto the stack frame
|
||||||
|
where they more reasonably belong. Unfortunately, this work will take some time
|
||||||
|
as it requires fairly substantial changes to the compiler.
|
||||||
|
|
||||||
|
Regardless, Rust programs don't need to worry about uninitialized values on
|
||||||
|
the stack for correctness. Although they might care for performance. Thankfully,
|
||||||
|
Rust makes it easy to take control here! Uninitialized values are there, and
|
||||||
|
you can work with them in Safe Rust, but you're never in danger.
|
127
src/doc/nomicon/dropck.md
Normal file
127
src/doc/nomicon/dropck.md
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
% Drop Check
|
||||||
|
|
||||||
|
We have seen how lifetimes provide us some fairly simple rules for ensuring
|
||||||
|
that never read dangling references. However up to this point we have only ever
|
||||||
|
interacted with the *outlives* relationship in an inclusive manner. That is,
|
||||||
|
when we talked about `'a: 'b`, it was ok for `'a` to live *exactly* as long as
|
||||||
|
`'b`. At first glance, this seems to be a meaningless distinction. Nothing ever
|
||||||
|
gets dropped at the same time as another, right? This is why we used the
|
||||||
|
following desugarring of `let` statements:
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
let x;
|
||||||
|
let y;
|
||||||
|
```
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
{
|
||||||
|
let x;
|
||||||
|
{
|
||||||
|
let y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Each creates its own scope, clearly establishing that one drops before the
|
||||||
|
other. However, what if we do the following?
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
let (x, y) = (vec![], vec![]);
|
||||||
|
```
|
||||||
|
|
||||||
|
Does either value strictly outlive the other? The answer is in fact *no*,
|
||||||
|
neither value strictly outlives the other. Of course, one of x or y will be
|
||||||
|
dropped before the other, but the actual order is not specified. Tuples aren't
|
||||||
|
special in this regard; composite structures just don't guarantee their
|
||||||
|
destruction order as of Rust 1.0.
|
||||||
|
|
||||||
|
We *could* specify this for the fields of built-in composites like tuples and
|
||||||
|
structs. However, what about something like Vec? Vec has to manually drop its
|
||||||
|
elements via pure-library code. In general, anything that implements Drop has
|
||||||
|
a chance to fiddle with its innards during its final death knell. Therefore
|
||||||
|
the compiler can't sufficiently reason about the actual destruction order
|
||||||
|
of the contents of any type that implements Drop.
|
||||||
|
|
||||||
|
So why do we care? We care because if the type system isn't careful, it could
|
||||||
|
accidentally make dangling pointers. Consider the following simple program:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
struct Inspector<'a>(&'a u8);
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let (inspector, days);
|
||||||
|
days = Box::new(1);
|
||||||
|
inspector = Inspector(&days);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This program is totally sound and compiles today. The fact that `days` does
|
||||||
|
not *strictly* outlive `inspector` doesn't matter. As long as the `inspector`
|
||||||
|
is alive, so is days.
|
||||||
|
|
||||||
|
However if we add a destructor, the program will no longer compile!
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
struct Inspector<'a>(&'a u8);
|
||||||
|
|
||||||
|
impl<'a> Drop for Inspector<'a> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
println!("I was only {} days from retirement!", self.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let (inspector, days);
|
||||||
|
days = Box::new(1);
|
||||||
|
inspector = Inspector(&days);
|
||||||
|
// Let's say `days` happens to get dropped first.
|
||||||
|
// Then when Inspector is dropped, it will try to read free'd memory!
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```text
|
||||||
|
<anon>:12:28: 12:32 error: `days` does not live long enough
|
||||||
|
<anon>:12 inspector = Inspector(&days);
|
||||||
|
^~~~
|
||||||
|
<anon>:9:11: 15:2 note: reference must be valid for the block at 9:10...
|
||||||
|
<anon>:9 fn main() {
|
||||||
|
<anon>:10 let (inspector, days);
|
||||||
|
<anon>:11 days = Box::new(1);
|
||||||
|
<anon>:12 inspector = Inspector(&days);
|
||||||
|
<anon>:13 // Let's say `days` happens to get dropped first.
|
||||||
|
<anon>:14 // Then when Inspector is dropped, it will try to read free'd memory!
|
||||||
|
...
|
||||||
|
<anon>:10:27: 15:2 note: ...but borrowed value is only valid for the block suffix following statement 0 at 10:26
|
||||||
|
<anon>:10 let (inspector, days);
|
||||||
|
<anon>:11 days = Box::new(1);
|
||||||
|
<anon>:12 inspector = Inspector(&days);
|
||||||
|
<anon>:13 // Let's say `days` happens to get dropped first.
|
||||||
|
<anon>:14 // Then when Inspector is dropped, it will try to read free'd memory!
|
||||||
|
<anon>:15 }
|
||||||
|
```
|
||||||
|
|
||||||
|
Implementing Drop lets the Inspector execute some arbitrary code during its
|
||||||
|
death. This means it can potentially observe that types that are supposed to
|
||||||
|
live as long as it does actually were destroyed first.
|
||||||
|
|
||||||
|
Interestingly, only generic types need to worry about this. If they aren't
|
||||||
|
generic, then the only lifetimes they can harbor are `'static`, which will truly
|
||||||
|
live *forever*. This is why this problem is referred to as *sound generic drop*.
|
||||||
|
Sound generic drop is enforced by the *drop checker*. As of this writing, some
|
||||||
|
of the finer details of how the drop checker validates types is totally up in
|
||||||
|
the air. However The Big Rule is the subtlety that we have focused on this whole
|
||||||
|
section:
|
||||||
|
|
||||||
|
**For a generic type to soundly implement drop, its generics arguments must
|
||||||
|
strictly outlive it.**
|
||||||
|
|
||||||
|
This rule is sufficient but not necessary to satisfy the drop checker. That is,
|
||||||
|
if your type obeys this rule then it's definitely sound to drop. However
|
||||||
|
there are special cases where you can fail to satisfy this, but still
|
||||||
|
successfully pass the borrow checker. These are the precise rules that are
|
||||||
|
currently up in the air.
|
||||||
|
|
||||||
|
It turns out that when writing unsafe code, we generally don't need to
|
||||||
|
worry at all about doing the right thing for the drop checker. However there
|
||||||
|
is one special case that you need to worry about, which we will look at in
|
||||||
|
the next section.
|
217
src/doc/nomicon/exception-safety.md
Normal file
217
src/doc/nomicon/exception-safety.md
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
% Exception Safety
|
||||||
|
|
||||||
|
Although programs should use unwinding sparingly, there's a lot of code that
|
||||||
|
*can* panic. If you unwrap a None, index out of bounds, or divide by 0, your
|
||||||
|
program will panic. On debug builds, every arithmetic operation can panic
|
||||||
|
if it overflows. Unless you are very careful and tightly control what code runs,
|
||||||
|
pretty much everything can unwind, and you need to be ready for it.
|
||||||
|
|
||||||
|
Being ready for unwinding is often referred to as *exception safety*
|
||||||
|
in the broader programming world. In Rust, there are two levels of exception
|
||||||
|
safety that one may concern themselves with:
|
||||||
|
|
||||||
|
* In unsafe code, we *must* be exception safe to the point of not violating
|
||||||
|
memory safety. We'll call this *minimal* exception safety.
|
||||||
|
|
||||||
|
* In safe code, it is *good* to be exception safe to the point of your program
|
||||||
|
doing the right thing. We'll call this *maximal* exception safety.
|
||||||
|
|
||||||
|
As is the case in many places in Rust, Unsafe code must be ready to deal with
|
||||||
|
bad Safe code when it comes to unwinding. Code that transiently creates
|
||||||
|
unsound states must be careful that a panic does not cause that state to be
|
||||||
|
used. Generally this means ensuring that only non-panicking code is run while
|
||||||
|
these states exist, or making a guard that cleans up the state in the case of
|
||||||
|
a panic. This does not necessarily mean that the state a panic witnesses is a
|
||||||
|
fully coherent state. We need only guarantee that it's a *safe* state.
|
||||||
|
|
||||||
|
Most Unsafe code is leaf-like, and therefore fairly easy to make exception-safe.
|
||||||
|
It controls all the code that runs, and most of that code can't panic. However
|
||||||
|
it is not uncommon for Unsafe code to work with arrays of temporarily
|
||||||
|
uninitialized data while repeatedly invoking caller-provided code. Such code
|
||||||
|
needs to be careful and consider exception safety.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Vec::push_all
|
||||||
|
|
||||||
|
`Vec::push_all` is a temporary hack to get extending a Vec by a slice reliably
|
||||||
|
efficient without specialization. Here's a simple implementation:
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
impl<T: Clone> Vec<T> {
|
||||||
|
fn push_all(&mut self, to_push: &[T]) {
|
||||||
|
self.reserve(to_push.len());
|
||||||
|
unsafe {
|
||||||
|
// can't overflow because we just reserved this
|
||||||
|
self.set_len(self.len() + to_push.len());
|
||||||
|
|
||||||
|
for (i, x) in to_push.iter().enumerate() {
|
||||||
|
self.ptr().offset(i as isize).write(x.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
We bypass `push` in order to avoid redundant capacity and `len` checks on the
|
||||||
|
Vec that we definitely know has capacity. The logic is totally correct, except
|
||||||
|
there's a subtle problem with our code: it's not exception-safe! `set_len`,
|
||||||
|
`offset`, and `write` are all fine; `clone` is the panic bomb we over-looked.
|
||||||
|
|
||||||
|
Clone is completely out of our control, and is totally free to panic. If it
|
||||||
|
does, our function will exit early with the length of the Vec set too large. If
|
||||||
|
the Vec is looked at or dropped, uninitialized memory will be read!
|
||||||
|
|
||||||
|
The fix in this case is fairly simple. If we want to guarantee that the values
|
||||||
|
we *did* clone are dropped, we can set the `len` every loop iteration. If we
|
||||||
|
just want to guarantee that uninitialized memory can't be observed, we can set
|
||||||
|
the `len` after the loop.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## BinaryHeap::sift_up
|
||||||
|
|
||||||
|
Bubbling an element up a heap is a bit more complicated than extending a Vec.
|
||||||
|
The pseudocode is as follows:
|
||||||
|
|
||||||
|
```text
|
||||||
|
bubble_up(heap, index):
|
||||||
|
while index != 0 && heap[index] < heap[parent(index)]:
|
||||||
|
heap.swap(index, parent(index))
|
||||||
|
index = parent(index)
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
A literal transcription of this code to Rust is totally fine, but has an annoying
|
||||||
|
performance characteristic: the `self` element is swapped over and over again
|
||||||
|
uselessly. We would rather have the following:
|
||||||
|
|
||||||
|
```text
|
||||||
|
bubble_up(heap, index):
|
||||||
|
let elem = heap[index]
|
||||||
|
while index != 0 && element < heap[parent(index)]:
|
||||||
|
heap[index] = heap[parent(index)]
|
||||||
|
index = parent(index)
|
||||||
|
heap[index] = elem
|
||||||
|
```
|
||||||
|
|
||||||
|
This code ensures that each element is copied as little as possible (it is in
|
||||||
|
fact necessary that elem be copied twice in general). However it now exposes
|
||||||
|
some exception safety trouble! At all times, there exists two copies of one
|
||||||
|
value. If we panic in this function something will be double-dropped.
|
||||||
|
Unfortunately, we also don't have full control of the code: that comparison is
|
||||||
|
user-defined!
|
||||||
|
|
||||||
|
Unlike Vec, the fix isn't as easy here. One option is to break the user-defined
|
||||||
|
code and the unsafe code into two separate phases:
|
||||||
|
|
||||||
|
```text
|
||||||
|
bubble_up(heap, index):
|
||||||
|
let end_index = index;
|
||||||
|
while end_index != 0 && heap[end_index] < heap[parent(end_index)]:
|
||||||
|
end_index = parent(end_index)
|
||||||
|
|
||||||
|
let elem = heap[index]
|
||||||
|
while index != end_index:
|
||||||
|
heap[index] = heap[parent(index)]
|
||||||
|
index = parent(index)
|
||||||
|
heap[index] = elem
|
||||||
|
```
|
||||||
|
|
||||||
|
If the user-defined code blows up, that's no problem anymore, because we haven't
|
||||||
|
actually touched the state of the heap yet. Once we do start messing with the
|
||||||
|
heap, we're working with only data and functions that we trust, so there's no
|
||||||
|
concern of panics.
|
||||||
|
|
||||||
|
Perhaps you're not happy with this design. Surely it's cheating! And we have
|
||||||
|
to do the complex heap traversal *twice*! Alright, let's bite the bullet. Let's
|
||||||
|
intermix untrusted and unsafe code *for reals*.
|
||||||
|
|
||||||
|
If Rust had `try` and `finally` like in Java, we could do the following:
|
||||||
|
|
||||||
|
```text
|
||||||
|
bubble_up(heap, index):
|
||||||
|
let elem = heap[index]
|
||||||
|
try:
|
||||||
|
while index != 0 && element < heap[parent(index)]:
|
||||||
|
heap[index] = heap[parent(index)]
|
||||||
|
index = parent(index)
|
||||||
|
finally:
|
||||||
|
heap[index] = elem
|
||||||
|
```
|
||||||
|
|
||||||
|
The basic idea is simple: if the comparison panics, we just toss the loose
|
||||||
|
element in the logically uninitialized index and bail out. Anyone who observes
|
||||||
|
the heap will see a potentially *inconsistent* heap, but at least it won't
|
||||||
|
cause any double-drops! If the algorithm terminates normally, then this
|
||||||
|
operation happens to coincide precisely with the how we finish up regardless.
|
||||||
|
|
||||||
|
Sadly, Rust has no such construct, so we're going to need to roll our own! The
|
||||||
|
way to do this is to store the algorithm's state in a separate struct with a
|
||||||
|
destructor for the "finally" logic. Whether we panic or not, that destructor
|
||||||
|
will run and clean up after us.
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
struct Hole<'a, T: 'a> {
|
||||||
|
data: &'a mut [T],
|
||||||
|
/// `elt` is always `Some` from new until drop.
|
||||||
|
elt: Option<T>,
|
||||||
|
pos: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T> Hole<'a, T> {
|
||||||
|
fn new(data: &'a mut [T], pos: usize) -> Self {
|
||||||
|
unsafe {
|
||||||
|
let elt = ptr::read(&data[pos]);
|
||||||
|
Hole {
|
||||||
|
data: data,
|
||||||
|
elt: Some(elt),
|
||||||
|
pos: pos,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pos(&self) -> usize { self.pos }
|
||||||
|
|
||||||
|
fn removed(&self) -> &T { self.elt.as_ref().unwrap() }
|
||||||
|
|
||||||
|
unsafe fn get(&self, index: usize) -> &T { &self.data[index] }
|
||||||
|
|
||||||
|
unsafe fn move_to(&mut self, index: usize) {
|
||||||
|
let index_ptr: *const _ = &self.data[index];
|
||||||
|
let hole_ptr = &mut self.data[self.pos];
|
||||||
|
ptr::copy_nonoverlapping(index_ptr, hole_ptr, 1);
|
||||||
|
self.pos = index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T> Drop for Hole<'a, T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
// fill the hole again
|
||||||
|
unsafe {
|
||||||
|
let pos = self.pos;
|
||||||
|
ptr::write(&mut self.data[pos], self.elt.take().unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Ord> BinaryHeap<T> {
|
||||||
|
fn sift_up(&mut self, pos: usize) {
|
||||||
|
unsafe {
|
||||||
|
// Take out the value at `pos` and create a hole.
|
||||||
|
let mut hole = Hole::new(&mut self.data, pos);
|
||||||
|
|
||||||
|
while hole.pos() != 0 {
|
||||||
|
let parent = parent(hole.pos());
|
||||||
|
if hole.removed() <= hole.get(parent) { break }
|
||||||
|
hole.move_to(parent);
|
||||||
|
}
|
||||||
|
// Hole will be unconditionally filled here; panic or not!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
137
src/doc/nomicon/exotic-sizes.md
Normal file
137
src/doc/nomicon/exotic-sizes.md
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
% Exotically Sized Types
|
||||||
|
|
||||||
|
Most of the time, we think in terms of types with a fixed, positive size. This
|
||||||
|
is not always the case, however.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Dynamically Sized Types (DSTs)
|
||||||
|
|
||||||
|
Rust in fact supports Dynamically Sized Types (DSTs): types without a statically
|
||||||
|
known size or alignment. On the surface, this is a bit nonsensical: Rust *must*
|
||||||
|
know the size and alignment of something in order to correctly work with it! In
|
||||||
|
this regard, DSTs are not normal types. Due to their lack of a statically known
|
||||||
|
size, these types can only exist behind some kind of pointer. Any pointer to a
|
||||||
|
DST consequently becomes a *fat* pointer consisting of the pointer and the
|
||||||
|
information that "completes" them (more on this below).
|
||||||
|
|
||||||
|
There are two major DSTs exposed by the language: trait objects, and slices.
|
||||||
|
|
||||||
|
A trait object represents some type that implements the traits it specifies.
|
||||||
|
The exact original type is *erased* in favour of runtime reflection
|
||||||
|
with a vtable containing all the information necessary to use the type.
|
||||||
|
This is the information that completes a trait object: a pointer to its vtable.
|
||||||
|
|
||||||
|
A slice is simply a view into some contiguous storage -- typically an array or
|
||||||
|
`Vec`. The information that completes a slice is just the number of elements
|
||||||
|
it points to.
|
||||||
|
|
||||||
|
Structs can actually store a single DST directly as their last field, but this
|
||||||
|
makes them a DST as well:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// Can't be stored on the stack directly
|
||||||
|
struct Foo {
|
||||||
|
info: u32,
|
||||||
|
data: [u8],
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**NOTE: [As of Rust 1.0 struct DSTs are broken if the last field has
|
||||||
|
a variable position based on its alignment][dst-issue].**
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Zero Sized Types (ZSTs)
|
||||||
|
|
||||||
|
Rust actually allows types to be specified that occupy no space:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
struct Foo; // No fields = no size
|
||||||
|
|
||||||
|
// All fields have no size = no size
|
||||||
|
struct Baz {
|
||||||
|
foo: Foo,
|
||||||
|
qux: (), // empty tuple has no size
|
||||||
|
baz: [u8; 0], // empty array has no size
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
On their own, Zero Sized Types (ZSTs) are, for obvious reasons, pretty useless.
|
||||||
|
However as with many curious layout choices in Rust, their potential is realized
|
||||||
|
in a generic context: Rust largely understands that any operation that produces
|
||||||
|
or stores a ZST can be reduced to a no-op. First off, storing it doesn't even
|
||||||
|
make sense -- it doesn't occupy any space. Also there's only one value of that
|
||||||
|
type, so anything that loads it can just produce it from the aether -- which is
|
||||||
|
also a no-op since it doesn't occupy any space.
|
||||||
|
|
||||||
|
One of the most extreme example's of this is Sets and Maps. Given a
|
||||||
|
`Map<Key, Value>`, it is common to implement a `Set<Key>` as just a thin wrapper
|
||||||
|
around `Map<Key, UselessJunk>`. In many languages, this would necessitate
|
||||||
|
allocating space for UselessJunk and doing work to store and load UselessJunk
|
||||||
|
only to discard it. Proving this unnecessary would be a difficult analysis for
|
||||||
|
the compiler.
|
||||||
|
|
||||||
|
However in Rust, we can just say that `Set<Key> = Map<Key, ()>`. Now Rust
|
||||||
|
statically knows that every load and store is useless, and no allocation has any
|
||||||
|
size. The result is that the monomorphized code is basically a custom
|
||||||
|
implementation of a HashSet with none of the overhead that HashMap would have to
|
||||||
|
support values.
|
||||||
|
|
||||||
|
Safe code need not worry about ZSTs, but *unsafe* code must be careful about the
|
||||||
|
consequence of types with no size. In particular, pointer offsets are no-ops,
|
||||||
|
and standard allocators (including jemalloc, the one used by default in Rust)
|
||||||
|
generally consider passing in `0` for the size of an allocation as Undefined
|
||||||
|
Behaviour.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Empty Types
|
||||||
|
|
||||||
|
Rust also enables types to be declared that *cannot even be instantiated*. These
|
||||||
|
types can only be talked about at the type level, and never at the value level.
|
||||||
|
Empty types can be declared by specifying an enum with no variants:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
enum Void {} // No variants = EMPTY
|
||||||
|
```
|
||||||
|
|
||||||
|
Empty types are even more marginal than ZSTs. The primary motivating example for
|
||||||
|
Void types is type-level unreachability. For instance, suppose an API needs to
|
||||||
|
return a Result in general, but a specific case actually is infallible. It's
|
||||||
|
actually possible to communicate this at the type level by returning a
|
||||||
|
`Result<T, Void>`. Consumers of the API can confidently unwrap such a Result
|
||||||
|
knowing that it's *statically impossible* for this value to be an `Err`, as
|
||||||
|
this would require providing a value of type `Void`.
|
||||||
|
|
||||||
|
In principle, Rust can do some interesting analyses and optimizations based
|
||||||
|
on this fact. For instance, `Result<T, Void>` could be represented as just `T`,
|
||||||
|
because the `Err` case doesn't actually exist. The following *could* also
|
||||||
|
compile:
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
enum Void {}
|
||||||
|
|
||||||
|
let res: Result<u32, Void> = Ok(0);
|
||||||
|
|
||||||
|
// Err doesn't exist anymore, so Ok is actually irrefutable.
|
||||||
|
let Ok(num) = res;
|
||||||
|
```
|
||||||
|
|
||||||
|
But neither of these tricks work today, so all Void types get you is
|
||||||
|
the ability to be confident that certain situations are statically impossible.
|
||||||
|
|
||||||
|
One final subtle detail about empty types is that raw pointers to them are
|
||||||
|
actually valid to construct, but dereferencing them is Undefined Behaviour
|
||||||
|
because that doesn't actually make sense. That is, you could model C's `void *`
|
||||||
|
type with `*const Void`, but this doesn't necessarily gain anything over using
|
||||||
|
e.g. `*const ()`, which *is* safe to randomly dereference.
|
||||||
|
|
||||||
|
|
||||||
|
[dst-issue]: https://github.com/rust-lang/rust/issues/26403
|
73
src/doc/nomicon/hrtb.md
Normal file
73
src/doc/nomicon/hrtb.md
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
% Higher-Rank Trait Bounds (HRTBs)
|
||||||
|
|
||||||
|
Rust's `Fn` traits are a little bit magic. For instance, we can write the
|
||||||
|
following code:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
struct Closure<F> {
|
||||||
|
data: (u8, u16),
|
||||||
|
func: F,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F> Closure<F>
|
||||||
|
where F: Fn(&(u8, u16)) -> &u8,
|
||||||
|
{
|
||||||
|
fn call(&self) -> &u8 {
|
||||||
|
(self.func)(&self.data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn do_it(data: &(u8, u16)) -> &u8 { &data.0 }
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let clo = Closure { data: (0, 1), func: do_it };
|
||||||
|
println!("{}", clo.call());
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
If we try to naively desugar this code in the same way that we did in the
|
||||||
|
lifetimes section, we run into some trouble:
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
struct Closure<F> {
|
||||||
|
data: (u8, u16),
|
||||||
|
func: F,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F> Closure<F>
|
||||||
|
// where F: Fn(&'??? (u8, u16)) -> &'??? u8,
|
||||||
|
{
|
||||||
|
fn call<'a>(&'a self) -> &'a u8 {
|
||||||
|
(self.func)(&self.data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn do_it<'b>(data: &'b (u8, u16)) -> &'b u8 { &'b data.0 }
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
'x: {
|
||||||
|
let clo = Closure { data: (0, 1), func: do_it };
|
||||||
|
println!("{}", clo.call());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
How on earth are we supposed to express the lifetimes on `F`'s trait bound? We
|
||||||
|
need to provide some lifetime there, but the lifetime we care about can't be
|
||||||
|
named until we enter the body of `call`! Also, that isn't some fixed lifetime;
|
||||||
|
`call` works with *any* lifetime `&self` happens to have at that point.
|
||||||
|
|
||||||
|
This job requires The Magic of Higher-Rank Trait Bounds (HRTBs). The way we
|
||||||
|
desugar this is as follows:
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
where for<'a> F: Fn(&'a (u8, u16)) -> &'a u8,
|
||||||
|
```
|
||||||
|
|
||||||
|
(Where `Fn(a, b, c) -> d` is itself just sugar for the unstable *real* `Fn`
|
||||||
|
trait)
|
||||||
|
|
||||||
|
`for<'a>` can be read as "for all choices of `'a`", and basically produces an
|
||||||
|
*infinite list* of trait bounds that F must satisfy. Intense. There aren't many
|
||||||
|
places outside of the `Fn` traits where we encounter HRTBs, and even for
|
||||||
|
those we have a nice magic sugar for the common cases.
|
252
src/doc/nomicon/leaking.md
Normal file
252
src/doc/nomicon/leaking.md
Normal file
@ -0,0 +1,252 @@
|
|||||||
|
% Leaking
|
||||||
|
|
||||||
|
Ownership-based resource management is intended to simplify composition. You
|
||||||
|
acquire resources when you create the object, and you release the resources when
|
||||||
|
it gets destroyed. Since destruction is handled for you, it means you can't
|
||||||
|
forget to release the resources, and it happens as soon as possible! Surely this
|
||||||
|
is perfect and all of our problems are solved.
|
||||||
|
|
||||||
|
Everything is terrible and we have new and exotic problems to try to solve.
|
||||||
|
|
||||||
|
Many people like to believe that Rust eliminates resource leaks. In practice,
|
||||||
|
this is basically true. You would be surprised to see a Safe Rust program
|
||||||
|
leak resources in an uncontrolled way.
|
||||||
|
|
||||||
|
However from a theoretical perspective this is absolutely not the case, no
|
||||||
|
matter how you look at it. In the strictest sense, "leaking" is so abstract as
|
||||||
|
to be unpreventable. It's quite trivial to initialize a collection at the start
|
||||||
|
of a program, fill it with tons of objects with destructors, and then enter an
|
||||||
|
infinite event loop that never refers to it. The collection will sit around
|
||||||
|
uselessly, holding on to its precious resources until the program terminates (at
|
||||||
|
which point all those resources would have been reclaimed by the OS anyway).
|
||||||
|
|
||||||
|
We may consider a more restricted form of leak: failing to drop a value that is
|
||||||
|
unreachable. Rust also doesn't prevent this. In fact Rust *has a function for
|
||||||
|
doing this*: `mem::forget`. This function consumes the value it is passed *and
|
||||||
|
then doesn't run its destructor*.
|
||||||
|
|
||||||
|
In the past `mem::forget` was marked as unsafe as a sort of lint against using
|
||||||
|
it, since failing to call a destructor is generally not a well-behaved thing to
|
||||||
|
do (though useful for some special unsafe code). However this was generally
|
||||||
|
determined to be an untenable stance to take: there are many ways to fail to
|
||||||
|
call a destructor in safe code. The most famous example is creating a cycle of
|
||||||
|
reference-counted pointers using interior mutability.
|
||||||
|
|
||||||
|
It is reasonable for safe code to assume that destructor leaks do not happen, as
|
||||||
|
any program that leaks destructors is probably wrong. However *unsafe* code
|
||||||
|
cannot rely on destructors to be run in order to be safe. For most types this
|
||||||
|
doesn't matter: if you leak the destructor then the type is by definition
|
||||||
|
inaccessible, so it doesn't matter, right? For instance, if you leak a `Box<u8>`
|
||||||
|
then you waste some memory but that's hardly going to violate memory-safety.
|
||||||
|
|
||||||
|
However where we must be careful with destructor leaks are *proxy* types. These
|
||||||
|
are types which manage access to a distinct object, but don't actually own it.
|
||||||
|
Proxy objects are quite rare. Proxy objects you'll need to care about are even
|
||||||
|
rarer. However we'll focus on three interesting examples in the standard
|
||||||
|
library:
|
||||||
|
|
||||||
|
* `vec::Drain`
|
||||||
|
* `Rc`
|
||||||
|
* `thread::scoped::JoinGuard`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Drain
|
||||||
|
|
||||||
|
`drain` is a collections API that moves data out of the container without
|
||||||
|
consuming the container. This enables us to reuse the allocation of a `Vec`
|
||||||
|
after claiming ownership over all of its contents. It produces an iterator
|
||||||
|
(Drain) that returns the contents of the Vec by-value.
|
||||||
|
|
||||||
|
Now, consider Drain in the middle of iteration: some values have been moved out,
|
||||||
|
and others haven't. This means that part of the Vec is now full of logically
|
||||||
|
uninitialized data! We could backshift all the elements in the Vec every time we
|
||||||
|
remove a value, but this would have pretty catastrophic performance
|
||||||
|
consequences.
|
||||||
|
|
||||||
|
Instead, we would like Drain to fix the Vec's backing storage when it is
|
||||||
|
dropped. It should run itself to completion, backshift any elements that weren't
|
||||||
|
removed (drain supports subranges), and then fix Vec's `len`. It's even
|
||||||
|
unwinding-safe! Easy!
|
||||||
|
|
||||||
|
Now consider the following:
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
let mut vec = vec![Box::new(0); 4];
|
||||||
|
|
||||||
|
{
|
||||||
|
// start draining, vec can no longer be accessed
|
||||||
|
let mut drainer = vec.drain(..);
|
||||||
|
|
||||||
|
// pull out two elements and immediately drop them
|
||||||
|
drainer.next();
|
||||||
|
drainer.next();
|
||||||
|
|
||||||
|
// get rid of drainer, but don't call its destructor
|
||||||
|
mem::forget(drainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Oops, vec[0] was dropped, we're reading a pointer into free'd memory!
|
||||||
|
println!("{}", vec[0]);
|
||||||
|
```
|
||||||
|
|
||||||
|
This is pretty clearly Not Good. Unfortunately, we're kind've stuck between a
|
||||||
|
rock and a hard place: maintaining consistent state at every step has an
|
||||||
|
enormous cost (and would negate any benefits of the API). Failing to maintain
|
||||||
|
consistent state gives us Undefined Behaviour in safe code (making the API
|
||||||
|
unsound).
|
||||||
|
|
||||||
|
So what can we do? Well, we can pick a trivially consistent state: set the Vec's
|
||||||
|
len to be 0 when we start the iteration, and fix it up if necessary in the
|
||||||
|
destructor. That way, if everything executes like normal we get the desired
|
||||||
|
behaviour with minimal overhead. But if someone has the *audacity* to
|
||||||
|
mem::forget us in the middle of the iteration, all that does is *leak even more*
|
||||||
|
(and possibly leave the Vec in an unexpected but otherwise consistent state).
|
||||||
|
Since we've accepted that mem::forget is safe, this is definitely safe. We call
|
||||||
|
leaks causing more leaks a *leak amplification*.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Rc
|
||||||
|
|
||||||
|
Rc is an interesting case because at first glance it doesn't appear to be a
|
||||||
|
proxy value at all. After all, it manages the data it points to, and dropping
|
||||||
|
all the Rcs for a value will drop that value. Leaking an Rc doesn't seem like it
|
||||||
|
would be particularly dangerous. It will leave the refcount permanently
|
||||||
|
incremented and prevent the data from being freed or dropped, but that seems
|
||||||
|
just like Box, right?
|
||||||
|
|
||||||
|
Nope.
|
||||||
|
|
||||||
|
Let's consider a simplified implementation of Rc:
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
struct Rc<T> {
|
||||||
|
ptr: *mut RcBox<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct RcBox<T> {
|
||||||
|
data: T,
|
||||||
|
ref_count: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Rc<T> {
|
||||||
|
fn new(data: T) -> Self {
|
||||||
|
unsafe {
|
||||||
|
// Wouldn't it be nice if heap::allocate worked like this?
|
||||||
|
let ptr = heap::allocate<RcBox<T>>();
|
||||||
|
ptr::write(ptr, RcBox {
|
||||||
|
data: data,
|
||||||
|
ref_count: 1,
|
||||||
|
});
|
||||||
|
Rc { ptr: ptr }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
unsafe {
|
||||||
|
(*self.ptr).ref_count += 1;
|
||||||
|
}
|
||||||
|
Rc { ptr: self.ptr }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Drop for Rc<T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
let inner = &mut ;
|
||||||
|
(*self.ptr).ref_count -= 1;
|
||||||
|
if (*self.ptr).ref_count == 0 {
|
||||||
|
// drop the data and then free it
|
||||||
|
ptr::read(self.ptr);
|
||||||
|
heap::deallocate(self.ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This code contains an implicit and subtle assumption: `ref_count` can fit in a
|
||||||
|
`usize`, because there can't be more than `usize::MAX` Rcs in memory. However
|
||||||
|
this itself assumes that the `ref_count` accurately reflects the number of Rcs
|
||||||
|
in memory, which we know is false with `mem::forget`. Using `mem::forget` we can
|
||||||
|
overflow the `ref_count`, and then get it down to 0 with outstanding Rcs. Then
|
||||||
|
we can happily use-after-free the inner data. Bad Bad Not Good.
|
||||||
|
|
||||||
|
This can be solved by just checking the `ref_count` and doing *something*. The
|
||||||
|
standard library's stance is to just abort, because your program has become
|
||||||
|
horribly degenerate. Also *oh my gosh* it's such a ridiculous corner case.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## thread::scoped::JoinGuard
|
||||||
|
|
||||||
|
The thread::scoped API intends to allow threads to be spawned that reference
|
||||||
|
data on their parent's stack without any synchronization over that data by
|
||||||
|
ensuring the parent joins the thread before any of the shared data goes out
|
||||||
|
of scope.
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
pub fn scoped<'a, F>(f: F) -> JoinGuard<'a>
|
||||||
|
where F: FnOnce() + Send + 'a
|
||||||
|
```
|
||||||
|
|
||||||
|
Here `f` is some closure for the other thread to execute. Saying that
|
||||||
|
`F: Send +'a` is saying that it closes over data that lives for `'a`, and it
|
||||||
|
either owns that data or the data was Sync (implying `&data` is Send).
|
||||||
|
|
||||||
|
Because JoinGuard has a lifetime, it keeps all the data it closes over
|
||||||
|
borrowed in the parent thread. This means the JoinGuard can't outlive
|
||||||
|
the data that the other thread is working on. When the JoinGuard *does* get
|
||||||
|
dropped it blocks the parent thread, ensuring the child terminates before any
|
||||||
|
of the closed-over data goes out of scope in the parent.
|
||||||
|
|
||||||
|
Usage looked like:
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
let mut data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
||||||
|
{
|
||||||
|
let guards = vec![];
|
||||||
|
for x in &mut data {
|
||||||
|
// Move the mutable reference into the closure, and execute
|
||||||
|
// it on a different thread. The closure has a lifetime bound
|
||||||
|
// by the lifetime of the mutable reference `x` we store in it.
|
||||||
|
// The guard that is returned is in turn assigned the lifetime
|
||||||
|
// of the closure, so it also mutably borrows `data` as `x` did.
|
||||||
|
// This means we cannot access `data` until the guard goes away.
|
||||||
|
let guard = thread::scoped(move || {
|
||||||
|
*x *= 2;
|
||||||
|
});
|
||||||
|
// store the thread's guard for later
|
||||||
|
guards.push(guard);
|
||||||
|
}
|
||||||
|
// All guards are dropped here, forcing the threads to join
|
||||||
|
// (this thread blocks here until the others terminate).
|
||||||
|
// Once the threads join, the borrow expires and the data becomes
|
||||||
|
// accessible again in this thread.
|
||||||
|
}
|
||||||
|
// data is definitely mutated here.
|
||||||
|
```
|
||||||
|
|
||||||
|
In principle, this totally works! Rust's ownership system perfectly ensures it!
|
||||||
|
...except it relies on a destructor being called to be safe.
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
let mut data = Box::new(0);
|
||||||
|
{
|
||||||
|
let guard = thread::scoped(|| {
|
||||||
|
// This is at best a data race. At worst, it's also a use-after-free.
|
||||||
|
*data += 1;
|
||||||
|
});
|
||||||
|
// Because the guard is forgotten, expiring the loan without blocking this
|
||||||
|
// thread.
|
||||||
|
mem::forget(guard);
|
||||||
|
}
|
||||||
|
// So the Box is dropped here while the scoped thread may or may not be trying
|
||||||
|
// to access it.
|
||||||
|
```
|
||||||
|
|
||||||
|
Dang. Here the destructor running was pretty fundamental to the API, and it had
|
||||||
|
to be scrapped in favour of a completely different design.
|
64
src/doc/nomicon/lifetime-elision.md
Normal file
64
src/doc/nomicon/lifetime-elision.md
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
% Lifetime Elision
|
||||||
|
|
||||||
|
In order to make common patterns more ergonomic, Rust allows lifetimes to be
|
||||||
|
*elided* in function signatures.
|
||||||
|
|
||||||
|
A *lifetime position* is anywhere you can write a lifetime in a type:
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
&'a T
|
||||||
|
&'a mut T
|
||||||
|
T<'a>
|
||||||
|
```
|
||||||
|
|
||||||
|
Lifetime positions can appear as either "input" or "output":
|
||||||
|
|
||||||
|
* For `fn` definitions, input refers to the types of the formal arguments
|
||||||
|
in the `fn` definition, while output refers to
|
||||||
|
result types. So `fn foo(s: &str) -> (&str, &str)` has elided one lifetime in
|
||||||
|
input position and two lifetimes in output position.
|
||||||
|
Note that the input positions of a `fn` method definition do not
|
||||||
|
include the lifetimes that occur in the method's `impl` header
|
||||||
|
(nor lifetimes that occur in the trait header, for a default method).
|
||||||
|
|
||||||
|
* In the future, it should be possible to elide `impl` headers in the same manner.
|
||||||
|
|
||||||
|
Elision rules are as follows:
|
||||||
|
|
||||||
|
* Each elided lifetime in input position becomes a distinct lifetime
|
||||||
|
parameter.
|
||||||
|
|
||||||
|
* If there is exactly one input lifetime position (elided or not), that lifetime
|
||||||
|
is assigned to *all* elided output lifetimes.
|
||||||
|
|
||||||
|
* If there are multiple input lifetime positions, but one of them is `&self` or
|
||||||
|
`&mut self`, the lifetime of `self` is assigned to *all* elided output lifetimes.
|
||||||
|
|
||||||
|
* Otherwise, it is an error to elide an output lifetime.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
fn print(s: &str); // elided
|
||||||
|
fn print<'a>(s: &'a str); // expanded
|
||||||
|
|
||||||
|
fn debug(lvl: uint, s: &str); // elided
|
||||||
|
fn debug<'a>(lvl: uint, s: &'a str); // expanded
|
||||||
|
|
||||||
|
fn substr(s: &str, until: uint) -> &str; // elided
|
||||||
|
fn substr<'a>(s: &'a str, until: uint) -> &'a str; // expanded
|
||||||
|
|
||||||
|
fn get_str() -> &str; // ILLEGAL
|
||||||
|
|
||||||
|
fn frob(s: &str, t: &str) -> &str; // ILLEGAL
|
||||||
|
|
||||||
|
fn get_mut(&mut self) -> &mut T; // elided
|
||||||
|
fn get_mut<'a>(&'a mut self) -> &'a mut T; // expanded
|
||||||
|
|
||||||
|
fn args<T:ToCStr>(&mut self, args: &[T]) -> &mut Command // elided
|
||||||
|
fn args<'a, 'b, T:ToCStr>(&'a mut self, args: &'b [T]) -> &'a mut Command // expanded
|
||||||
|
|
||||||
|
fn new(buf: &mut [u8]) -> BufWriter; // elided
|
||||||
|
fn new<'a>(buf: &'a mut [u8]) -> BufWriter<'a> // expanded
|
||||||
|
|
||||||
|
```
|
81
src/doc/nomicon/lifetime-mismatch.md
Normal file
81
src/doc/nomicon/lifetime-mismatch.md
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
% Limits of Lifetimes
|
||||||
|
|
||||||
|
Given the following code:
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
struct Foo;
|
||||||
|
|
||||||
|
impl Foo {
|
||||||
|
fn mutate_and_share(&mut self) -> &Self { &*self }
|
||||||
|
fn share(&self) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut foo = Foo;
|
||||||
|
let loan = foo.mutate_and_share();
|
||||||
|
foo.share();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
One might expect it to compile. We call `mutate_and_share`, which mutably borrows
|
||||||
|
`foo` temporarily, but then returns only a shared reference. Therefore we
|
||||||
|
would expect `foo.share()` to succeed as `foo` shouldn't be mutably borrowed.
|
||||||
|
|
||||||
|
However when we try to compile it:
|
||||||
|
|
||||||
|
```text
|
||||||
|
<anon>:11:5: 11:8 error: cannot borrow `foo` as immutable because it is also borrowed as mutable
|
||||||
|
<anon>:11 foo.share();
|
||||||
|
^~~
|
||||||
|
<anon>:10:16: 10:19 note: previous borrow of `foo` occurs here; the mutable borrow prevents subsequent moves, borrows, or modification of `foo` until the borrow ends
|
||||||
|
<anon>:10 let loan = foo.mutate_and_share();
|
||||||
|
^~~
|
||||||
|
<anon>:12:2: 12:2 note: previous borrow ends here
|
||||||
|
<anon>:8 fn main() {
|
||||||
|
<anon>:9 let mut foo = Foo;
|
||||||
|
<anon>:10 let loan = foo.mutate_and_share();
|
||||||
|
<anon>:11 foo.share();
|
||||||
|
<anon>:12 }
|
||||||
|
^
|
||||||
|
```
|
||||||
|
|
||||||
|
What happened? Well, we got the exact same reasoning as we did for
|
||||||
|
[Example 2 in the previous section][ex2]. We desugar the program and we get
|
||||||
|
the following:
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
struct Foo;
|
||||||
|
|
||||||
|
impl Foo {
|
||||||
|
fn mutate_and_share<'a>(&'a mut self) -> &'a Self { &'a *self }
|
||||||
|
fn share<'a>(&'a self) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
'b: {
|
||||||
|
let mut foo: Foo = Foo;
|
||||||
|
'c: {
|
||||||
|
let loan: &'c Foo = Foo::mutate_and_share::<'c>(&'c mut foo);
|
||||||
|
'd: {
|
||||||
|
Foo::share::<'d>(&'d foo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The lifetime system is forced to extend the `&mut foo` to have lifetime `'c`,
|
||||||
|
due to the lifetime of `loan` and mutate_and_share's signature. Then when we
|
||||||
|
try to call `share`, and it sees we're trying to alias that `&'c mut foo` and
|
||||||
|
blows up in our face!
|
||||||
|
|
||||||
|
This program is clearly correct according to the reference semantics we actually
|
||||||
|
care about, but the lifetime system is too coarse-grained to handle that.
|
||||||
|
|
||||||
|
|
||||||
|
TODO: other common problems? SEME regions stuff, mostly?
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[ex2]: lifetimes.html#example-2:-aliasing-a-mutable-reference
|
215
src/doc/nomicon/lifetimes.md
Normal file
215
src/doc/nomicon/lifetimes.md
Normal file
@ -0,0 +1,215 @@
|
|||||||
|
% Lifetimes
|
||||||
|
|
||||||
|
Rust enforces these rules through *lifetimes*. Lifetimes are effectively
|
||||||
|
just names for scopes somewhere in the program. Each reference,
|
||||||
|
and anything that contains a reference, is tagged with a lifetime specifying
|
||||||
|
the scope it's valid for.
|
||||||
|
|
||||||
|
Within a function body, Rust generally doesn't let you explicitly name the
|
||||||
|
lifetimes involved. This is because it's generally not really necessary
|
||||||
|
to talk about lifetimes in a local context; Rust has all the information and
|
||||||
|
can work out everything as optimally as possible. Many anonymous scopes and
|
||||||
|
temporaries that you would otherwise have to write are often introduced to
|
||||||
|
make your code Just Work.
|
||||||
|
|
||||||
|
However once you cross the function boundary, you need to start talking about
|
||||||
|
lifetimes. Lifetimes are denoted with an apostrophe: `'a`, `'static`. To dip
|
||||||
|
our toes with lifetimes, we're going to pretend that we're actually allowed
|
||||||
|
to label scopes with lifetimes, and desugar the examples from the start of
|
||||||
|
this chapter.
|
||||||
|
|
||||||
|
Originally, our examples made use of *aggressive* sugar -- high fructose corn
|
||||||
|
syrup even -- around scopes and lifetimes, because writing everything out
|
||||||
|
explicitly is *extremely noisy*. All Rust code relies on aggressive inference
|
||||||
|
and elision of "obvious" things.
|
||||||
|
|
||||||
|
One particularly interesting piece of sugar is that each `let` statement implicitly
|
||||||
|
introduces a scope. For the most part, this doesn't really matter. However it
|
||||||
|
does matter for variables that refer to each other. As a simple example, let's
|
||||||
|
completely desugar this simple piece of Rust code:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let x = 0;
|
||||||
|
let y = &x;
|
||||||
|
let z = &y;
|
||||||
|
```
|
||||||
|
|
||||||
|
The borrow checker always tries to minimize the extent of a lifetime, so it will
|
||||||
|
likely desugar to the following:
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
// NOTE: `'a: {` and `&'b x` is not valid syntax!
|
||||||
|
'a: {
|
||||||
|
let x: i32 = 0;
|
||||||
|
'b: {
|
||||||
|
// lifetime used is 'b because that's good enough.
|
||||||
|
let y: &'b i32 = &'b x;
|
||||||
|
'c: {
|
||||||
|
// ditto on 'c
|
||||||
|
let z: &'c &'b i32 = &'c y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Wow. That’s... awful. Let’s all take a moment to thank Rust for making this easier.
|
||||||
|
|
||||||
|
Actually passing references to outer scopes will cause Rust to infer
|
||||||
|
a larger lifetime:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let x = 0;
|
||||||
|
let z;
|
||||||
|
let y = &x;
|
||||||
|
z = y;
|
||||||
|
```
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
'a: {
|
||||||
|
let x: i32 = 0;
|
||||||
|
'b: {
|
||||||
|
let z: &'b i32;
|
||||||
|
'c: {
|
||||||
|
// Must use 'b here because this reference is
|
||||||
|
// being passed to that scope.
|
||||||
|
let y: &'b i32 = &'b x;
|
||||||
|
z = y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Example: references that outlive referents
|
||||||
|
|
||||||
|
Alright, let's look at some of those examples from before:
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
fn as_str(data: &u32) -> &str {
|
||||||
|
let s = format!("{}", data);
|
||||||
|
&s
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
desugars to:
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
fn as_str<'a>(data: &'a u32) -> &'a str {
|
||||||
|
'b: {
|
||||||
|
let s = format!("{}", data);
|
||||||
|
return &'a s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This signature of `as_str` takes a reference to a u32 with *some* lifetime, and
|
||||||
|
promises that it can produce a reference to a str that can live *just as long*.
|
||||||
|
Already we can see why this signature might be trouble. That basically implies
|
||||||
|
that we're going to find a str somewhere in the scope the reference
|
||||||
|
to the u32 originated in, or somewhere *even earlier*. That's a bit of a big
|
||||||
|
ask.
|
||||||
|
|
||||||
|
We then proceed to compute the string `s`, and return a reference to it. Since
|
||||||
|
the contract of our function says the reference must outlive `'a`, that's the
|
||||||
|
lifetime we infer for the reference. Unfortunately, `s` was defined in the
|
||||||
|
scope `'b`, so the only way this is sound is if `'b` contains `'a` -- which is
|
||||||
|
clearly false since `'a` must contain the function call itself. We have therefore
|
||||||
|
created a reference whose lifetime outlives its referent, which is *literally*
|
||||||
|
the first thing we said that references can't do. The compiler rightfully blows
|
||||||
|
up in our face.
|
||||||
|
|
||||||
|
To make this more clear, we can expand the example:
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
fn as_str<'a>(data: &'a u32) -> &'a str {
|
||||||
|
'b: {
|
||||||
|
let s = format!("{}", data);
|
||||||
|
return &'a s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
'c: {
|
||||||
|
let x: u32 = 0;
|
||||||
|
'd: {
|
||||||
|
// An anonymous scope is introduced because the borrow does not
|
||||||
|
// need to last for the whole scope x is valid for. The return
|
||||||
|
// of as_str must find a str somewhere before this function
|
||||||
|
// call. Obviously not happening.
|
||||||
|
println!("{}", as_str::<'d>(&'d x));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Shoot!
|
||||||
|
|
||||||
|
Of course, the right way to write this function is as follows:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn to_string(data: &u32) -> String {
|
||||||
|
format!("{}", data)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
We must produce an owned value inside the function to return it! The only way
|
||||||
|
we could have returned an `&'a str` would have been if it was in a field of the
|
||||||
|
`&'a u32`, which is obviously not the case.
|
||||||
|
|
||||||
|
(Actually we could have also just returned a string literal, which as a global
|
||||||
|
can be considered to reside at the bottom of the stack; though this limits
|
||||||
|
our implementation *just a bit*.)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Example: aliasing a mutable reference
|
||||||
|
|
||||||
|
How about the other example:
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
let mut data = vec![1, 2, 3];
|
||||||
|
let x = &data[0];
|
||||||
|
data.push(4);
|
||||||
|
println!("{}", x);
|
||||||
|
```
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
'a: {
|
||||||
|
let mut data: Vec<i32> = vec![1, 2, 3];
|
||||||
|
'b: {
|
||||||
|
// 'b is as big as we need this borrow to be
|
||||||
|
// (just need to get to `println!`)
|
||||||
|
let x: &'b i32 = Index::index::<'b>(&'b data, 0);
|
||||||
|
'c: {
|
||||||
|
// Temporary scope because we don't need the
|
||||||
|
// &mut to last any longer.
|
||||||
|
Vec::push(&'c mut data, 4);
|
||||||
|
}
|
||||||
|
println!("{}", x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The problem here is is bit more subtle and interesting. We want Rust to
|
||||||
|
reject this program for the following reason: We have a live shared reference `x`
|
||||||
|
to a descendent of `data` when we try to take a mutable reference to `data`
|
||||||
|
to `push`. This would create an aliased mutable reference, which would
|
||||||
|
violate the *second* rule of references.
|
||||||
|
|
||||||
|
However this is *not at all* how Rust reasons that this program is bad. Rust
|
||||||
|
doesn't understand that `x` is a reference to a subpath of `data`. It doesn't
|
||||||
|
understand Vec at all. What it *does* see is that `x` has to live for `'b` to
|
||||||
|
be printed. The signature of `Index::index` subsequently demands that the
|
||||||
|
reference we take to `data` has to survive for `'b`. When we try to call `push`,
|
||||||
|
it then sees us try to make an `&'c mut data`. Rust knows that `'c` is contained
|
||||||
|
within `'b`, and rejects our program because the `&'b data` must still be live!
|
||||||
|
|
||||||
|
Here we see that the lifetime system is much more coarse than the reference
|
||||||
|
semantics we're actually interested in preserving. For the most part, *that's
|
||||||
|
totally ok*, because it keeps us from spending all day explaining our program
|
||||||
|
to the compiler. However it does mean that several programs that are totally
|
||||||
|
correct with respect to Rust's *true* semantics are rejected because lifetimes
|
||||||
|
are too dumb.
|
98
src/doc/nomicon/meet-safe-and-unsafe.md
Normal file
98
src/doc/nomicon/meet-safe-and-unsafe.md
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
% Meet Safe and Unsafe
|
||||||
|
|
||||||
|
Programmers in safe "high-level" languages face a fundamental dilemma. On one
|
||||||
|
hand, it would be *really* great to just say what you want and not worry about
|
||||||
|
how it's done. On the other hand, that can lead to unacceptably poor
|
||||||
|
performance. It may be necessary to drop down to less clear or idiomatic
|
||||||
|
practices to get the performance characteristics you want. Or maybe you just
|
||||||
|
throw up your hands in disgust and decide to shell out to an implementation in
|
||||||
|
a less sugary-wonderful *unsafe* language.
|
||||||
|
|
||||||
|
Worse, when you want to talk directly to the operating system, you *have* to
|
||||||
|
talk to an unsafe language: *C*. C is ever-present and unavoidable. It's the
|
||||||
|
lingua-franca of the programming world.
|
||||||
|
Even other safe languages generally expose C interfaces for the world at large!
|
||||||
|
Regardless of why you're doing it, as soon as your program starts talking to
|
||||||
|
C it stops being safe.
|
||||||
|
|
||||||
|
With that said, Rust is *totally* a safe programming language.
|
||||||
|
|
||||||
|
Well, Rust *has* a safe programming language. Let's step back a bit.
|
||||||
|
|
||||||
|
Rust can be thought of as being composed of two programming languages: *Safe
|
||||||
|
Rust* and *Unsafe Rust*. Safe Rust is For Reals Totally Safe. Unsafe Rust,
|
||||||
|
unsurprisingly, is *not* For Reals Totally Safe. In fact, Unsafe Rust lets you
|
||||||
|
do some really crazy unsafe things.
|
||||||
|
|
||||||
|
Safe Rust is the *true* Rust programming language. If all you do is write Safe
|
||||||
|
Rust, you will never have to worry about type-safety or memory-safety. You will
|
||||||
|
never endure a null or dangling pointer, or any of that Undefined Behaviour
|
||||||
|
nonsense.
|
||||||
|
|
||||||
|
*That's totally awesome.*
|
||||||
|
|
||||||
|
The standard library also gives you enough utilities out-of-the-box that you'll
|
||||||
|
be able to write awesome high-performance applications and libraries in pure
|
||||||
|
idiomatic Safe Rust.
|
||||||
|
|
||||||
|
But maybe you want to talk to another language. Maybe you're writing a
|
||||||
|
low-level abstraction not exposed by the standard library. Maybe you're
|
||||||
|
*writing* the standard library (which is written entirely in Rust). Maybe you
|
||||||
|
need to do something the type-system doesn't understand and just *frob some dang
|
||||||
|
bits*. Maybe you need Unsafe Rust.
|
||||||
|
|
||||||
|
Unsafe Rust is exactly like Safe Rust with all the same rules and semantics.
|
||||||
|
However Unsafe Rust lets you do some *extra* things that are Definitely Not Safe.
|
||||||
|
|
||||||
|
The only things that are different in Unsafe Rust are that you can:
|
||||||
|
|
||||||
|
* Dereference raw pointers
|
||||||
|
* Call `unsafe` functions (including C functions, intrinsics, and the raw allocator)
|
||||||
|
* Implement `unsafe` traits
|
||||||
|
* Mutate statics
|
||||||
|
|
||||||
|
That's it. The reason these operations are relegated to Unsafe is that misusing
|
||||||
|
any of these things will cause the ever dreaded Undefined Behaviour. Invoking
|
||||||
|
Undefined Behaviour gives the compiler full rights to do arbitrarily bad things
|
||||||
|
to your program. You definitely *should not* invoke Undefined Behaviour.
|
||||||
|
|
||||||
|
Unlike C, Undefined Behaviour is pretty limited in scope in Rust. All the core
|
||||||
|
language cares about is preventing the following things:
|
||||||
|
|
||||||
|
* Dereferencing null or dangling pointers
|
||||||
|
* Reading [uninitialized memory][]
|
||||||
|
* Breaking the [pointer aliasing rules][]
|
||||||
|
* Producing invalid primitive values:
|
||||||
|
* dangling/null references
|
||||||
|
* a `bool` that isn't 0 or 1
|
||||||
|
* an undefined `enum` discriminant
|
||||||
|
* a `char` outside the ranges [0x0, 0xD7FF] and [0xE000, 0x10FFFF]
|
||||||
|
* A non-utf8 `str`
|
||||||
|
* Unwinding into another language
|
||||||
|
* Causing a [data race][race]
|
||||||
|
|
||||||
|
That's it. That's all the causes of Undefined Behaviour baked into Rust. Of
|
||||||
|
course, unsafe functions and traits are free to declare arbitrary other
|
||||||
|
constraints that a program must maintain to avoid Undefined Behaviour. However,
|
||||||
|
generally violations of these constraints will just transitively lead to one of
|
||||||
|
the above problems. Some additional constraints may also derive from compiler
|
||||||
|
intrinsics that make special assumptions about how code can be optimized.
|
||||||
|
|
||||||
|
Rust is otherwise quite permissive with respect to other dubious operations.
|
||||||
|
Rust considers it "safe" to:
|
||||||
|
|
||||||
|
* Deadlock
|
||||||
|
* Have a [race condition][race]
|
||||||
|
* Leak memory
|
||||||
|
* Fail to call destructors
|
||||||
|
* Overflow integers
|
||||||
|
* Abort the program
|
||||||
|
* Delete the production database
|
||||||
|
|
||||||
|
However any program that actually manages to do such a thing is *probably*
|
||||||
|
incorrect. Rust provides lots of tools to make these things rare, but
|
||||||
|
these problems are considered impractical to categorically prevent.
|
||||||
|
|
||||||
|
[pointer aliasing rules]: references.html
|
||||||
|
[uninitialized memory]: uninitialized.html
|
||||||
|
[race]: races.html
|
14
src/doc/nomicon/obrm.md
Normal file
14
src/doc/nomicon/obrm.md
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
% The Perils Of Ownership Based Resource Management (OBRM)
|
||||||
|
|
||||||
|
OBRM (AKA RAII: Resource Acquisition Is Initialization) is something you'll
|
||||||
|
interact with a lot in Rust. Especially if you use the standard library.
|
||||||
|
|
||||||
|
Roughly speaking the pattern is as follows: to acquire a resource, you create an
|
||||||
|
object that manages it. To release the resource, you simply destroy the object,
|
||||||
|
and it cleans up the resource for you. The most common "resource" this pattern
|
||||||
|
manages is simply *memory*. `Box`, `Rc`, and basically everything in
|
||||||
|
`std::collections` is a convenience to enable correctly managing memory. This is
|
||||||
|
particularly important in Rust because we have no pervasive GC to rely on for
|
||||||
|
memory management. Which is the point, really: Rust is about control. However we
|
||||||
|
are not limited to just memory. Pretty much every other system resource like a
|
||||||
|
thread, file, or socket is exposed through this kind of API.
|
76
src/doc/nomicon/other-reprs.md
Normal file
76
src/doc/nomicon/other-reprs.md
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
% Alternative representations
|
||||||
|
|
||||||
|
Rust allows you to specify alternative data layout strategies from the default.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# repr(C)
|
||||||
|
|
||||||
|
This is the most important `repr`. It has fairly simple intent: do what C does.
|
||||||
|
The order, size, and alignment of fields is exactly what you would expect from C
|
||||||
|
or C++. Any type you expect to pass through an FFI boundary should have
|
||||||
|
`repr(C)`, as C is the lingua-franca of the programming world. This is also
|
||||||
|
necessary to soundly do more elaborate tricks with data layout such as
|
||||||
|
reinterpreting values as a different type.
|
||||||
|
|
||||||
|
However, the interaction with Rust's more exotic data layout features must be
|
||||||
|
kept in mind. Due to its dual purpose as "for FFI" and "for layout control",
|
||||||
|
`repr(C)` can be applied to types that will be nonsensical or problematic if
|
||||||
|
passed through the FFI boundary.
|
||||||
|
|
||||||
|
* ZSTs are still zero-sized, even though this is not a standard behaviour in
|
||||||
|
C, and is explicitly contrary to the behaviour of an empty type in C++, which
|
||||||
|
still consumes a byte of space.
|
||||||
|
|
||||||
|
* DSTs, tuples, and tagged unions are not a concept in C and as such are never
|
||||||
|
FFI safe.
|
||||||
|
|
||||||
|
* **If the type would have any [drop flags][], they will still be added**
|
||||||
|
|
||||||
|
* This is equivalent to one of `repr(u*)` (see the next section) for enums. The
|
||||||
|
chosen size is the default enum size for the target platform's C ABI. Note that
|
||||||
|
enum representation in C is implementation defined, so this is really a "best
|
||||||
|
guess". In particular, this may be incorrect when the C code of interest is
|
||||||
|
compiled with certain flags.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# repr(u8), repr(u16), repr(u32), repr(u64)
|
||||||
|
|
||||||
|
These specify the size to make a C-like enum. If the discriminant overflows the
|
||||||
|
integer it has to fit in, it will produce a compile-time error. You can manually
|
||||||
|
ask Rust to allow this by setting the overflowing element to explicitly be 0.
|
||||||
|
However Rust will not allow you to create an enum where two variants have the
|
||||||
|
same discriminant.
|
||||||
|
|
||||||
|
On non-C-like enums, this will inhibit certain optimizations like the null-
|
||||||
|
pointer optimization.
|
||||||
|
|
||||||
|
These reprs have no effect on a struct.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# repr(packed)
|
||||||
|
|
||||||
|
`repr(packed)` forces rust to strip any padding, and only align the type to a
|
||||||
|
byte. This may improve the memory footprint, but will likely have other negative
|
||||||
|
side-effects.
|
||||||
|
|
||||||
|
In particular, most architectures *strongly* prefer values to be aligned. This
|
||||||
|
may mean the unaligned loads are penalized (x86), or even fault (some ARM
|
||||||
|
chips). For simple cases like directly loading or storing a packed field, the
|
||||||
|
compiler might be able to paper over alignment issues with shifts and masks.
|
||||||
|
However if you take a reference to a packed field, it's unlikely that the
|
||||||
|
compiler will be able to emit code to avoid an unaligned load.
|
||||||
|
|
||||||
|
**[As of Rust 1.0 this can cause undefined behaviour.][ub loads]**
|
||||||
|
|
||||||
|
`repr(packed)` is not to be used lightly. Unless you have extreme requirements,
|
||||||
|
this should not be used.
|
||||||
|
|
||||||
|
This repr is a modifier on `repr(C)` and `repr(rust)`.
|
||||||
|
|
||||||
|
[drop flags]: drop-flags.html
|
||||||
|
[ub loads]: https://github.com/rust-lang/rust/issues/27060
|
67
src/doc/nomicon/ownership.md
Normal file
67
src/doc/nomicon/ownership.md
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
% Ownership and Lifetimes
|
||||||
|
|
||||||
|
Ownership is the breakout feature of Rust. It allows Rust to be completely
|
||||||
|
memory-safe and efficient, while avoiding garbage collection. Before getting
|
||||||
|
into the ownership system in detail, we will consider the motivation of this
|
||||||
|
design.
|
||||||
|
|
||||||
|
We will assume that you accept that garbage collection (GC) is not always an
|
||||||
|
optimal solution, and that it is desirable to manually manage memory in some
|
||||||
|
contexts. If you do not accept this, might I interest you in a different
|
||||||
|
language?
|
||||||
|
|
||||||
|
Regardless of your feelings on GC, it is pretty clearly a *massive* boon to
|
||||||
|
making code safe. You never have to worry about things going away *too soon*
|
||||||
|
(although whether you still wanted to be pointing at that thing is a different
|
||||||
|
issue...). This is a pervasive problem that C and C++ programs need to deal
|
||||||
|
with. Consider this simple mistake that all of us who have used a non-GC'd
|
||||||
|
language have made at one point:
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
fn as_str(data: &u32) -> &str {
|
||||||
|
// compute the string
|
||||||
|
let s = format!("{}", data);
|
||||||
|
|
||||||
|
// OH NO! We returned a reference to something that
|
||||||
|
// exists only in this function!
|
||||||
|
// Dangling pointer! Use after free! Alas!
|
||||||
|
// (this does not compile in Rust)
|
||||||
|
&s
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This is exactly what Rust's ownership system was built to solve.
|
||||||
|
Rust knows the scope in which the `&s` lives, and as such can prevent it from
|
||||||
|
escaping. However this is a simple case that even a C compiler could plausibly
|
||||||
|
catch. Things get more complicated as code gets bigger and pointers get fed through
|
||||||
|
various functions. Eventually, a C compiler will fall down and won't be able to
|
||||||
|
perform sufficient escape analysis to prove your code unsound. It will consequently
|
||||||
|
be forced to accept your program on the assumption that it is correct.
|
||||||
|
|
||||||
|
This will never happen to Rust. It's up to the programmer to prove to the
|
||||||
|
compiler that everything is sound.
|
||||||
|
|
||||||
|
Of course, Rust's story around ownership is much more complicated than just
|
||||||
|
verifying that references don't escape the scope of their referent. That's
|
||||||
|
because ensuring pointers are always valid is much more complicated than this.
|
||||||
|
For instance in this code,
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
let mut data = vec![1, 2, 3];
|
||||||
|
// get an internal reference
|
||||||
|
let x = &data[0];
|
||||||
|
|
||||||
|
// OH NO! `push` causes the backing storage of `data` to be reallocated.
|
||||||
|
// Dangling pointer! User after free! Alas!
|
||||||
|
// (this does not compile in Rust)
|
||||||
|
data.push(4);
|
||||||
|
|
||||||
|
println!("{}", x);
|
||||||
|
```
|
||||||
|
|
||||||
|
naive scope analysis would be insufficient to prevent this bug, because `data`
|
||||||
|
does in fact live as long as we needed. However it was *changed* while we had
|
||||||
|
a reference into it. This is why Rust requires any references to freeze the
|
||||||
|
referent and its owners.
|
||||||
|
|
||||||
|
|
87
src/doc/nomicon/phantom-data.md
Normal file
87
src/doc/nomicon/phantom-data.md
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
% PhantomData
|
||||||
|
|
||||||
|
When working with unsafe code, we can often end up in a situation where
|
||||||
|
types or lifetimes are logically associated with a struct, but not actually
|
||||||
|
part of a field. This most commonly occurs with lifetimes. For instance, the
|
||||||
|
`Iter` for `&'a [T]` is (approximately) defined as follows:
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
struct Iter<'a, T: 'a> {
|
||||||
|
ptr: *const T,
|
||||||
|
end: *const T,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
However because `'a` is unused within the struct's body, it's *unbounded*.
|
||||||
|
Because of the troubles this has historically caused, unbounded lifetimes and
|
||||||
|
types are *forbidden* in struct definitions. Therefore we must somehow refer
|
||||||
|
to these types in the body. Correctly doing this is necessary to have
|
||||||
|
correct variance and drop checking.
|
||||||
|
|
||||||
|
We do this using `PhantomData`, which is a special marker type. `PhantomData`
|
||||||
|
consumes no space, but simulates a field of the given type for the purpose of
|
||||||
|
static analysis. This was deemed to be less error-prone than explicitly telling
|
||||||
|
the type-system the kind of variance that you want, while also providing other
|
||||||
|
useful such as the information needed by drop check.
|
||||||
|
|
||||||
|
Iter logically contains a bunch of `&'a T`s, so this is exactly what we tell
|
||||||
|
the PhantomData to simulate:
|
||||||
|
|
||||||
|
```
|
||||||
|
use std::marker;
|
||||||
|
|
||||||
|
struct Iter<'a, T: 'a> {
|
||||||
|
ptr: *const T,
|
||||||
|
end: *const T,
|
||||||
|
_marker: marker::PhantomData<&'a T>,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
and that's it. The lifetime will be bounded, and your iterator will be variant
|
||||||
|
over `'a` and `T`. Everything Just Works.
|
||||||
|
|
||||||
|
Another important example is Vec, which is (approximately) defined as follows:
|
||||||
|
|
||||||
|
```
|
||||||
|
struct Vec<T> {
|
||||||
|
data: *const T, // *const for variance!
|
||||||
|
len: usize,
|
||||||
|
cap: usize,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Unlike the previous example it *appears* that everything is exactly as we
|
||||||
|
want. Every generic argument to Vec shows up in the at least one field.
|
||||||
|
Good to go!
|
||||||
|
|
||||||
|
Nope.
|
||||||
|
|
||||||
|
The drop checker will generously determine that Vec<T> does not own any values
|
||||||
|
of type T. This will in turn make it conclude that it doesn't need to worry
|
||||||
|
about Vec dropping any T's in its destructor for determining drop check
|
||||||
|
soundness. This will in turn allow people to create unsoundness using
|
||||||
|
Vec's destructor.
|
||||||
|
|
||||||
|
In order to tell dropck that we *do* own values of type T, and therefore may
|
||||||
|
drop some T's when *we* drop, we must add an extra PhantomData saying exactly
|
||||||
|
that:
|
||||||
|
|
||||||
|
```
|
||||||
|
use std::marker;
|
||||||
|
|
||||||
|
struct Vec<T> {
|
||||||
|
data: *const T, // *const for covariance!
|
||||||
|
len: usize,
|
||||||
|
cap: usize,
|
||||||
|
_marker: marker::PhantomData<T>,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Raw pointers that own an allocation is such a pervasive pattern that the
|
||||||
|
standard library made a utility for itself called `Unique<T>` which:
|
||||||
|
|
||||||
|
* wraps a `*const T` for variance
|
||||||
|
* includes a `PhantomData<T>`,
|
||||||
|
* auto-derives Send/Sync as if T was contained
|
||||||
|
* marks the pointer as NonZero for the null-pointer optimization
|
||||||
|
|
35
src/doc/nomicon/poisoning.md
Normal file
35
src/doc/nomicon/poisoning.md
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
% Poisoning
|
||||||
|
|
||||||
|
Although all unsafe code *must* ensure it has minimal exception safety, not all
|
||||||
|
types ensure *maximal* exception safety. Even if the type does, your code may
|
||||||
|
ascribe additional meaning to it. For instance, an integer is certainly
|
||||||
|
exception-safe, but has no semantics on its own. It's possible that code that
|
||||||
|
panics could fail to correctly update the integer, producing an inconsistent
|
||||||
|
program state.
|
||||||
|
|
||||||
|
This is *usually* fine, because anything that witnesses an exception is about
|
||||||
|
to get destroyed. For instance, if you send a Vec to another thread and that
|
||||||
|
thread panics, it doesn't matter if the Vec is in a weird state. It will be
|
||||||
|
dropped and go away forever. However some types are especially good at smuggling
|
||||||
|
values across the panic boundary.
|
||||||
|
|
||||||
|
These types may choose to explicitly *poison* themselves if they witness a panic.
|
||||||
|
Poisoning doesn't entail anything in particular. Generally it just means
|
||||||
|
preventing normal usage from proceeding. The most notable example of this is the
|
||||||
|
standard library's Mutex type. A Mutex will poison itself if one of its
|
||||||
|
MutexGuards (the thing it returns when a lock is obtained) is dropped during a
|
||||||
|
panic. Any future attempts to lock the Mutex will return an `Err` or panic.
|
||||||
|
|
||||||
|
Mutex poisons not for true safety in the sense that Rust normally cares about. It
|
||||||
|
poisons as a safety-guard against blindly using the data that comes out of a Mutex
|
||||||
|
that has witnessed a panic while locked. The data in such a Mutex was likely in the
|
||||||
|
middle of being modified, and as such may be in an inconsistent or incomplete state.
|
||||||
|
It is important to note that one cannot violate memory safety with such a type
|
||||||
|
if it is correctly written. After all, it must be minimally exception-safe!
|
||||||
|
|
||||||
|
However if the Mutex contained, say, a BinaryHeap that does not actually have the
|
||||||
|
heap property, it's unlikely that any code that uses it will do
|
||||||
|
what the author intended. As such, the program should not proceed normally.
|
||||||
|
Still, if you're double-plus-sure that you can do *something* with the value,
|
||||||
|
the Mutex exposes a method to get the lock anyway. It *is* safe, after all.
|
||||||
|
Just maybe nonsense.
|
86
src/doc/nomicon/races.md
Normal file
86
src/doc/nomicon/races.md
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
% Data Races and Race Conditions
|
||||||
|
|
||||||
|
Safe Rust guarantees an absence of data races, which are defined as:
|
||||||
|
|
||||||
|
* two or more threads concurrently accessing a location of memory
|
||||||
|
* one of them is a write
|
||||||
|
* one of them is unsynchronized
|
||||||
|
|
||||||
|
A data race has Undefined Behaviour, and is therefore impossible to perform
|
||||||
|
in Safe Rust. Data races are *mostly* prevented through rust's ownership system:
|
||||||
|
it's impossible to alias a mutable reference, so it's impossible to perform a
|
||||||
|
data race. Interior mutability makes this more complicated, which is largely why
|
||||||
|
we have the Send and Sync traits (see below).
|
||||||
|
|
||||||
|
**However Rust does not prevent general race conditions.**
|
||||||
|
|
||||||
|
This is pretty fundamentally impossible, and probably honestly undesirable. Your
|
||||||
|
hardware is racy, your OS is racy, the other programs on your computer are racy,
|
||||||
|
and the world this all runs in is racy. Any system that could genuinely claim to
|
||||||
|
prevent *all* race conditions would be pretty awful to use, if not just
|
||||||
|
incorrect.
|
||||||
|
|
||||||
|
So it's perfectly "fine" for a Safe Rust program to get deadlocked or do
|
||||||
|
something incredibly stupid with incorrect synchronization. Obviously such a
|
||||||
|
program isn't very good, but Rust can only hold your hand so far. Still, a
|
||||||
|
race condition can't violate memory safety in a Rust program on
|
||||||
|
its own. Only in conjunction with some other unsafe code can a race condition
|
||||||
|
actually violate memory safety. For instance:
|
||||||
|
|
||||||
|
```rust,no_run
|
||||||
|
use std::thread;
|
||||||
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
let data = vec![1, 2, 3, 4];
|
||||||
|
// Arc so that the memory the AtomicUsize is stored in still exists for
|
||||||
|
// the other thread to increment, even if we completely finish executing
|
||||||
|
// before it. Rust won't compile the program without it, because of the
|
||||||
|
// lifetime requirements of thread::spawn!
|
||||||
|
let idx = Arc::new(AtomicUsize::new(0));
|
||||||
|
let other_idx = idx.clone();
|
||||||
|
|
||||||
|
// `move` captures other_idx by-value, moving it into this thread
|
||||||
|
thread::spawn(move || {
|
||||||
|
// It's ok to mutate idx because this value
|
||||||
|
// is an atomic, so it can't cause a Data Race.
|
||||||
|
other_idx.fetch_add(10, Ordering::SeqCst);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Index with the value loaded from the atomic. This is safe because we
|
||||||
|
// read the atomic memory only once, and then pass a copy of that value
|
||||||
|
// to the Vec's indexing implementation. This indexing will be correctly
|
||||||
|
// bounds checked, and there's no chance of the value getting changed
|
||||||
|
// in the middle. However our program may panic if the thread we spawned
|
||||||
|
// managed to increment before this ran. A race condition because correct
|
||||||
|
// program execution (panicing is rarely correct) depends on order of
|
||||||
|
// thread execution.
|
||||||
|
println!("{}", data[idx.load(Ordering::SeqCst)]);
|
||||||
|
```
|
||||||
|
|
||||||
|
```rust,no_run
|
||||||
|
use std::thread;
|
||||||
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
let data = vec![1, 2, 3, 4];
|
||||||
|
|
||||||
|
let idx = Arc::new(AtomicUsize::new(0));
|
||||||
|
let other_idx = idx.clone();
|
||||||
|
|
||||||
|
// `move` captures other_idx by-value, moving it into this thread
|
||||||
|
thread::spawn(move || {
|
||||||
|
// It's ok to mutate idx because this value
|
||||||
|
// is an atomic, so it can't cause a Data Race.
|
||||||
|
other_idx.fetch_add(10, Ordering::SeqCst);
|
||||||
|
});
|
||||||
|
|
||||||
|
if idx.load(Ordering::SeqCst) < data.len() {
|
||||||
|
unsafe {
|
||||||
|
// Incorrectly loading the idx after we did the bounds check.
|
||||||
|
// It could have changed. This is a race condition, *and dangerous*
|
||||||
|
// because we decided to do `get_unchecked`, which is `unsafe`.
|
||||||
|
println!("{}", data.get_unchecked(idx.load(Ordering::SeqCst)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
177
src/doc/nomicon/references.md
Normal file
177
src/doc/nomicon/references.md
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
% References
|
||||||
|
|
||||||
|
This section gives a high-level view of the memory model that *all* Rust
|
||||||
|
programs must satisfy to be correct. Safe code is statically verified
|
||||||
|
to obey this model by the borrow checker. Unsafe code may go above
|
||||||
|
and beyond the borrow checker while still satisfying this model. The borrow
|
||||||
|
checker may also be extended to allow more programs to compile, as long as
|
||||||
|
this more fundamental model is satisfied.
|
||||||
|
|
||||||
|
There are two kinds of reference:
|
||||||
|
|
||||||
|
* Shared reference: `&`
|
||||||
|
* Mutable reference: `&mut`
|
||||||
|
|
||||||
|
Which obey the following rules:
|
||||||
|
|
||||||
|
* A reference cannot outlive its referent
|
||||||
|
* A mutable reference cannot be aliased
|
||||||
|
|
||||||
|
That's it. That's the whole model. Of course, we should probably define
|
||||||
|
what *aliased* means. To define aliasing, we must define the notion of
|
||||||
|
*paths* and *liveness*.
|
||||||
|
|
||||||
|
|
||||||
|
**NOTE: The model that follows is generally agreed to be dubious and have
|
||||||
|
issues. It's ok-ish as an intuitive model, but fails to capture the desired
|
||||||
|
semantics. We leave this here to be able to use notions introduced here in later
|
||||||
|
sections. This will be significantly changed in the future. TODO: do that.**
|
||||||
|
|
||||||
|
|
||||||
|
# Paths
|
||||||
|
|
||||||
|
If all Rust had were values (no pointers), then every value would be uniquely
|
||||||
|
owned by a variable or composite structure. From this we naturally derive a
|
||||||
|
*tree* of ownership. The stack itself is the root of the tree, with every
|
||||||
|
variable as its direct children. Each variable's direct children would be their
|
||||||
|
fields (if any), and so on.
|
||||||
|
|
||||||
|
From this view, every value in Rust has a unique *path* in the tree of
|
||||||
|
ownership. Of particular interest are *ancestors* and *descendants*: if `x` owns
|
||||||
|
`y`, then `x` is an ancestor of `y`, and `y` is a descendant of `x`. Note
|
||||||
|
that this is an inclusive relationship: `x` is a descendant and ancestor of
|
||||||
|
itself.
|
||||||
|
|
||||||
|
We can then define references as simply *names* for paths. When you create a
|
||||||
|
reference, you're declaring that an ownership path exists to this address
|
||||||
|
of memory.
|
||||||
|
|
||||||
|
Tragically, plenty of data doesn't reside on the stack, and we must also
|
||||||
|
accommodate this. Globals and thread-locals are simple enough to model as
|
||||||
|
residing at the bottom of the stack (though we must be careful with mutable
|
||||||
|
globals). Data on the heap poses a different problem.
|
||||||
|
|
||||||
|
If all Rust had on the heap was data uniquely owned by a pointer on the stack,
|
||||||
|
then we could just treat such a pointer as a struct that owns the value on the
|
||||||
|
heap. Box, Vec, String, and HashMap, are examples of types which uniquely
|
||||||
|
own data on the heap.
|
||||||
|
|
||||||
|
Unfortunately, data on the heap is not *always* uniquely owned. Rc for instance
|
||||||
|
introduces a notion of *shared* ownership. Shared ownership of a value means
|
||||||
|
there is no unique path to it. A value with no unique path limits what we can do
|
||||||
|
with it.
|
||||||
|
|
||||||
|
In general, only shared references can be created to non-unique paths. However
|
||||||
|
mechanisms which ensure mutual exclusion may establish One True Owner
|
||||||
|
temporarily, establishing a unique path to that value (and therefore all
|
||||||
|
its children). If this is done, the value may be mutated. In particular, a
|
||||||
|
mutable reference can be taken.
|
||||||
|
|
||||||
|
The most common way to establish such a path is through *interior mutability*,
|
||||||
|
in contrast to the *inherited mutability* that everything in Rust normally uses.
|
||||||
|
Cell, RefCell, Mutex, and RWLock are all examples of interior mutability types.
|
||||||
|
These types provide exclusive access through runtime restrictions.
|
||||||
|
|
||||||
|
An interesting case of this effect is Rc itself: if an Rc has refcount 1,
|
||||||
|
then it is safe to mutate or even move its internals. Note however that the
|
||||||
|
refcount itself uses interior mutability.
|
||||||
|
|
||||||
|
In order to correctly communicate to the type system that a variable or field of
|
||||||
|
a struct can have interior mutability, it must be wrapped in an UnsafeCell. This
|
||||||
|
does not in itself make it safe to perform interior mutability operations on
|
||||||
|
that value. You still must yourself ensure that mutual exclusion is upheld.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Liveness
|
||||||
|
|
||||||
|
Note: Liveness is not the same thing as a *lifetime*, which will be explained
|
||||||
|
in detail in the next section of this chapter.
|
||||||
|
|
||||||
|
Roughly, a reference is *live* at some point in a program if it can be
|
||||||
|
dereferenced. Shared references are always live unless they are literally
|
||||||
|
unreachable (for instance, they reside in freed or leaked memory). Mutable
|
||||||
|
references can be reachable but *not* live through the process of *reborrowing*.
|
||||||
|
|
||||||
|
A mutable reference can be reborrowed to either a shared or mutable reference to
|
||||||
|
one of its descendants. A reborrowed reference will only be live again once all
|
||||||
|
reborrows derived from it expire. For instance, a mutable reference can be
|
||||||
|
reborrowed to point to a field of its referent:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let x = &mut (1, 2);
|
||||||
|
{
|
||||||
|
// reborrow x to a subfield
|
||||||
|
let y = &mut x.0;
|
||||||
|
// y is now live, but x isn't
|
||||||
|
*y = 3;
|
||||||
|
}
|
||||||
|
// y goes out of scope, so x is live again
|
||||||
|
*x = (5, 7);
|
||||||
|
```
|
||||||
|
|
||||||
|
It is also possible to reborrow into *multiple* mutable references, as long as
|
||||||
|
they are *disjoint*: no reference is an ancestor of another. Rust
|
||||||
|
explicitly enables this to be done with disjoint struct fields, because
|
||||||
|
disjointness can be statically proven:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let x = &mut (1, 2);
|
||||||
|
{
|
||||||
|
// reborrow x to two disjoint subfields
|
||||||
|
let y = &mut x.0;
|
||||||
|
let z = &mut x.1;
|
||||||
|
|
||||||
|
// y and z are now live, but x isn't
|
||||||
|
*y = 3;
|
||||||
|
*z = 4;
|
||||||
|
}
|
||||||
|
// y and z go out of scope, so x is live again
|
||||||
|
*x = (5, 7);
|
||||||
|
```
|
||||||
|
|
||||||
|
However it's often the case that Rust isn't sufficiently smart to prove that
|
||||||
|
multiple borrows are disjoint. *This does not mean it is fundamentally illegal
|
||||||
|
to make such a borrow*, just that Rust isn't as smart as you want.
|
||||||
|
|
||||||
|
To simplify things, we can model variables as a fake type of reference: *owned*
|
||||||
|
references. Owned references have much the same semantics as mutable references:
|
||||||
|
they can be re-borrowed in a mutable or shared manner, which makes them no
|
||||||
|
longer live. Live owned references have the unique property that they can be
|
||||||
|
moved out of (though mutable references *can* be swapped out of). This power is
|
||||||
|
only given to *live* owned references because moving its referent would of
|
||||||
|
course invalidate all outstanding references prematurely.
|
||||||
|
|
||||||
|
As a local lint against inappropriate mutation, only variables that are marked
|
||||||
|
as `mut` can be borrowed mutably.
|
||||||
|
|
||||||
|
It is interesting to note that Box behaves exactly like an owned reference. It
|
||||||
|
can be moved out of, and Rust understands it sufficiently to reason about its
|
||||||
|
paths like a normal variable.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Aliasing
|
||||||
|
|
||||||
|
With liveness and paths defined, we can now properly define *aliasing*:
|
||||||
|
|
||||||
|
**A mutable reference is aliased if there exists another live reference to one
|
||||||
|
of its ancestors or descendants.**
|
||||||
|
|
||||||
|
(If you prefer, you may also say the two live references alias *each other*.
|
||||||
|
This has no semantic consequences, but is probably a more useful notion when
|
||||||
|
verifying the soundness of a construct.)
|
||||||
|
|
||||||
|
That's it. Super simple right? Except for the fact that it took us two pages to
|
||||||
|
define all of the terms in that definition. You know: Super. Simple.
|
||||||
|
|
||||||
|
Actually it's a bit more complicated than that. In addition to references, Rust
|
||||||
|
has *raw pointers*: `*const T` and `*mut T`. Raw pointers have no inherent
|
||||||
|
ownership or aliasing semantics. As a result, Rust makes absolutely no effort to
|
||||||
|
track that they are used correctly, and they are wildly unsafe.
|
||||||
|
|
||||||
|
**It is an open question to what degree raw pointers have alias semantics.
|
||||||
|
However it is important for these definitions to be sound that the existence of
|
||||||
|
a raw pointer does not imply some kind of live path.**
|
152
src/doc/nomicon/repr-rust.md
Normal file
152
src/doc/nomicon/repr-rust.md
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
% repr(Rust)
|
||||||
|
|
||||||
|
First and foremost, all types have an alignment specified in bytes. The
|
||||||
|
alignment of a type specifies what addresses are valid to store the value at. A
|
||||||
|
value of alignment `n` must only be stored at an address that is a multiple of
|
||||||
|
`n`. So alignment 2 means you must be stored at an even address, and 1 means
|
||||||
|
that you can be stored anywhere. Alignment is at least 1, and always a power of
|
||||||
|
2. Most primitives are generally aligned to their size, although this is
|
||||||
|
platform-specific behaviour. In particular, on x86 `u64` and `f64` may be only
|
||||||
|
aligned to 32 bits.
|
||||||
|
|
||||||
|
A type's size must always be a multiple of its alignment. This ensures that an
|
||||||
|
array of that type may always be indexed by offsetting by a multiple of its
|
||||||
|
size. Note that the size and alignment of a type may not be known
|
||||||
|
statically in the case of [dynamically sized types][dst].
|
||||||
|
|
||||||
|
Rust gives you the following ways to lay out composite data:
|
||||||
|
|
||||||
|
* structs (named product types)
|
||||||
|
* tuples (anonymous product types)
|
||||||
|
* arrays (homogeneous product types)
|
||||||
|
* enums (named sum types -- tagged unions)
|
||||||
|
|
||||||
|
An enum is said to be *C-like* if none of its variants have associated data.
|
||||||
|
|
||||||
|
Composite structures will have an alignment equal to the maximum
|
||||||
|
of their fields' alignment. Rust will consequently insert padding where
|
||||||
|
necessary to ensure that all fields are properly aligned and that the overall
|
||||||
|
type's size is a multiple of its alignment. For instance:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
struct A {
|
||||||
|
a: u8,
|
||||||
|
b: u32,
|
||||||
|
c: u16,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
will be 32-bit aligned assuming these primitives are aligned to their size.
|
||||||
|
It will therefore have a size that is a multiple of 32-bits. It will potentially
|
||||||
|
*really* become:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
struct A {
|
||||||
|
a: u8,
|
||||||
|
_pad1: [u8; 3], // to align `b`
|
||||||
|
b: u32,
|
||||||
|
c: u16,
|
||||||
|
_pad2: [u8; 2], // to make overall size multiple of 4
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
There is *no indirection* for these types; all data is stored contiguously as
|
||||||
|
you would expect in C. However with the exception of arrays (which are densely
|
||||||
|
packed and in-order), the layout of data is not by default specified in Rust.
|
||||||
|
Given the two following struct definitions:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
struct A {
|
||||||
|
a: i32,
|
||||||
|
b: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct B {
|
||||||
|
x: i32,
|
||||||
|
b: u64,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Rust *does* guarantee that two instances of A have their data laid out in
|
||||||
|
exactly the same way. However Rust *does not* guarantee that an instance of A
|
||||||
|
has the same field ordering or padding as an instance of B (in practice there's
|
||||||
|
no particular reason why they wouldn't, other than that its not currently
|
||||||
|
guaranteed).
|
||||||
|
|
||||||
|
With A and B as written, this is basically nonsensical, but several other
|
||||||
|
features of Rust make it desirable for the language to play with data layout in
|
||||||
|
complex ways.
|
||||||
|
|
||||||
|
For instance, consider this struct:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
struct Foo<T, U> {
|
||||||
|
count: u16,
|
||||||
|
data1: T,
|
||||||
|
data2: U,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Now consider the monomorphizations of `Foo<u32, u16>` and `Foo<u16, u32>`. If
|
||||||
|
Rust lays out the fields in the order specified, we expect it to pad the
|
||||||
|
values in the struct to satisfy their alignment requirements. So if Rust
|
||||||
|
didn't reorder fields, we would expect it to produce the following:
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
struct Foo<u16, u32> {
|
||||||
|
count: u16,
|
||||||
|
data1: u16,
|
||||||
|
data2: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Foo<u32, u16> {
|
||||||
|
count: u16,
|
||||||
|
_pad1: u16,
|
||||||
|
data1: u32,
|
||||||
|
data2: u16,
|
||||||
|
_pad2: u16,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The latter case quite simply wastes space. An optimal use of space therefore
|
||||||
|
requires different monomorphizations to have *different field orderings*.
|
||||||
|
|
||||||
|
**Note: this is a hypothetical optimization that is not yet implemented in Rust
|
||||||
|
1.0**
|
||||||
|
|
||||||
|
Enums make this consideration even more complicated. Naively, an enum such as:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
enum Foo {
|
||||||
|
A(u32),
|
||||||
|
B(u64),
|
||||||
|
C(u8),
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
would be laid out as:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
struct FooRepr {
|
||||||
|
data: u64, // this is either a u64, u32, or u8 based on `tag`
|
||||||
|
tag: u8, // 0 = A, 1 = B, 2 = C
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
And indeed this is approximately how it would be laid out in general
|
||||||
|
(modulo the size and position of `tag`). However there are several cases where
|
||||||
|
such a representation is inefficient. The classic case of this is Rust's
|
||||||
|
"null pointer optimization". Given a pointer that is known to not be null
|
||||||
|
(e.g. `&u32`), an enum can *store* a discriminant bit *inside* the pointer
|
||||||
|
by using null as a special value. The net result is that
|
||||||
|
`size_of::<Option<&T>>() == size_of::<&T>()`
|
||||||
|
|
||||||
|
There are many types in Rust that are, or contain, "not null" pointers such as
|
||||||
|
`Box<T>`, `Vec<T>`, `String`, `&T`, and `&mut T`. Similarly, one can imagine
|
||||||
|
nested enums pooling their tags into a single discriminant, as they are by
|
||||||
|
definition known to have a limited range of valid values. In principle enums can
|
||||||
|
use fairly elaborate algorithms to cache bits throughout nested types with
|
||||||
|
special constrained representations. As such it is *especially* desirable that
|
||||||
|
we leave enum layout unspecified today.
|
||||||
|
|
||||||
|
[dst]: exotic-sizes.html#dynamically-sized-types-(dsts)
|
150
src/doc/nomicon/safe-unsafe-meaning.md
Normal file
150
src/doc/nomicon/safe-unsafe-meaning.md
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
% How Safe and Unsafe Interact
|
||||||
|
|
||||||
|
So what's the relationship between Safe and Unsafe Rust? How do they interact?
|
||||||
|
|
||||||
|
Rust models the separation between Safe and Unsafe Rust with the `unsafe`
|
||||||
|
keyword, which can be thought as a sort of *foreign function interface* (FFI)
|
||||||
|
between Safe and Unsafe Rust. This is the magic behind why we can say Safe Rust
|
||||||
|
is a safe language: all the scary unsafe bits are relegated exclusively to FFI
|
||||||
|
*just like every other safe language*.
|
||||||
|
|
||||||
|
However because one language is a subset of the other, the two can be cleanly
|
||||||
|
intermixed as long as the boundary between Safe and Unsafe Rust is denoted with
|
||||||
|
the `unsafe` keyword. No need to write headers, initialize runtimes, or any of
|
||||||
|
that other FFI boiler-plate.
|
||||||
|
|
||||||
|
There are several places `unsafe` can appear in Rust today, which can largely be
|
||||||
|
grouped into two categories:
|
||||||
|
|
||||||
|
* There are unchecked contracts here. To declare you understand this, I require
|
||||||
|
you to write `unsafe` elsewhere:
|
||||||
|
* On functions, `unsafe` is declaring the function to be unsafe to call.
|
||||||
|
Users of the function must check the documentation to determine what this
|
||||||
|
means, and then have to write `unsafe` somewhere to identify that they're
|
||||||
|
aware of the danger.
|
||||||
|
* On trait declarations, `unsafe` is declaring that *implementing* the trait
|
||||||
|
is an unsafe operation, as it has contracts that other unsafe code is free
|
||||||
|
to trust blindly. (More on this below.)
|
||||||
|
|
||||||
|
* I am declaring that I have, to the best of my knowledge, adhered to the
|
||||||
|
unchecked contracts:
|
||||||
|
* On trait implementations, `unsafe` is declaring that the contract of the
|
||||||
|
`unsafe` trait has been upheld.
|
||||||
|
* On blocks, `unsafe` is declaring any unsafety from an unsafe
|
||||||
|
operation within to be handled, and therefore the parent function is safe.
|
||||||
|
|
||||||
|
There is also `#[unsafe_no_drop_flag]`, which is a special case that exists for
|
||||||
|
historical reasons and is in the process of being phased out. See the section on
|
||||||
|
[drop flags][] for details.
|
||||||
|
|
||||||
|
Some examples of unsafe functions:
|
||||||
|
|
||||||
|
* `slice::get_unchecked` will perform unchecked indexing, allowing memory
|
||||||
|
safety to be freely violated.
|
||||||
|
* `ptr::offset` is an intrinsic that invokes Undefined Behaviour if it is
|
||||||
|
not "in bounds" as defined by LLVM.
|
||||||
|
* `mem::transmute` reinterprets some value as having the given type,
|
||||||
|
bypassing type safety in arbitrary ways. (see [conversions][] for details)
|
||||||
|
* All FFI functions are `unsafe` because they can do arbitrary things.
|
||||||
|
C being an obvious culprit, but generally any language can do something
|
||||||
|
that Rust isn't happy about.
|
||||||
|
|
||||||
|
As of Rust 1.0 there are exactly two unsafe traits:
|
||||||
|
|
||||||
|
* `Send` is a marker trait (it has no actual API) that promises implementors
|
||||||
|
are safe to send (move) to another thread.
|
||||||
|
* `Sync` is a marker trait that promises that threads can safely share
|
||||||
|
implementors through a shared reference.
|
||||||
|
|
||||||
|
The need for unsafe traits boils down to the fundamental property of safe code:
|
||||||
|
|
||||||
|
**No matter how completely awful Safe code is, it can't cause Undefined
|
||||||
|
Behaviour.**
|
||||||
|
|
||||||
|
This means that Unsafe Rust, **the royal vanguard of Undefined Behaviour**, has to be
|
||||||
|
*super paranoid* about generic safe code. To be clear, Unsafe Rust is totally free to trust
|
||||||
|
specific safe code. Anything else would degenerate into infinite spirals of
|
||||||
|
paranoid despair. In particular it's generally regarded as ok to trust the standard library
|
||||||
|
to be correct. `std` is effectively an extension of the language, and you
|
||||||
|
really just have to trust the language. If `std` fails to uphold the
|
||||||
|
guarantees it declares, then it's basically a language bug.
|
||||||
|
|
||||||
|
That said, it would be best to minimize *needlessly* relying on properties of
|
||||||
|
concrete safe code. Bugs happen! Of course, I must reinforce that this is only
|
||||||
|
a concern for Unsafe code. Safe code can blindly trust anyone and everyone
|
||||||
|
as far as basic memory-safety is concerned.
|
||||||
|
|
||||||
|
On the other hand, safe traits are free to declare arbitrary contracts, but because
|
||||||
|
implementing them is safe, unsafe code can't trust those contracts to actually
|
||||||
|
be upheld. This is different from the concrete case because *anyone* can
|
||||||
|
randomly implement the interface. There is something fundamentally different
|
||||||
|
about trusting a particular piece of code to be correct, and trusting *all the
|
||||||
|
code that will ever be written* to be correct.
|
||||||
|
|
||||||
|
For instance Rust has `PartialOrd` and `Ord` traits to try to differentiate
|
||||||
|
between types which can "just" be compared, and those that actually implement a
|
||||||
|
total ordering. Pretty much every API that wants to work with data that can be
|
||||||
|
compared wants Ord data. For instance, a sorted map like BTreeMap
|
||||||
|
*doesn't even make sense* for partially ordered types. If you claim to implement
|
||||||
|
Ord for a type, but don't actually provide a proper total ordering, BTreeMap will
|
||||||
|
get *really confused* and start making a total mess of itself. Data that is
|
||||||
|
inserted may be impossible to find!
|
||||||
|
|
||||||
|
But that's okay. BTreeMap is safe, so it guarantees that even if you give it a
|
||||||
|
completely garbage Ord implementation, it will still do something *safe*. You
|
||||||
|
won't start reading uninitialized or unallocated memory. In fact, BTreeMap
|
||||||
|
manages to not actually lose any of your data. When the map is dropped, all the
|
||||||
|
destructors will be successfully called! Hooray!
|
||||||
|
|
||||||
|
However BTreeMap is implemented using a modest spoonful of Unsafe Rust (most collections
|
||||||
|
are). That means that it's not necessarily *trivially true* that a bad Ord
|
||||||
|
implementation will make BTreeMap behave safely. BTreeMap must be sure not to rely
|
||||||
|
on Ord *where safety is at stake*. Ord is provided by safe code, and safety is not
|
||||||
|
safe code's responsibility to uphold.
|
||||||
|
|
||||||
|
But wouldn't it be grand if there was some way for Unsafe to trust some trait
|
||||||
|
contracts *somewhere*? This is the problem that unsafe traits tackle: by marking
|
||||||
|
*the trait itself* as unsafe to implement, unsafe code can trust the implementation
|
||||||
|
to uphold the trait's contract. Although the trait implementation may be
|
||||||
|
incorrect in arbitrary other ways.
|
||||||
|
|
||||||
|
For instance, given a hypothetical UnsafeOrd trait, this is technically a valid
|
||||||
|
implementation:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
# use std::cmp::Ordering;
|
||||||
|
# struct MyType;
|
||||||
|
# unsafe trait UnsafeOrd { fn cmp(&self, other: &Self) -> Ordering; }
|
||||||
|
unsafe impl UnsafeOrd for MyType {
|
||||||
|
fn cmp(&self, other: &Self) -> Ordering {
|
||||||
|
Ordering::Equal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
But it's probably not the implementation you want.
|
||||||
|
|
||||||
|
Rust has traditionally avoided making traits unsafe because it makes Unsafe
|
||||||
|
pervasive, which is not desirable. Send and Sync are unsafe is because thread
|
||||||
|
safety is a *fundamental property* that unsafe code cannot possibly hope to defend
|
||||||
|
against in the same way it would defend against a bad Ord implementation. The
|
||||||
|
only way to possibly defend against thread-unsafety would be to *not use
|
||||||
|
threading at all*. Making every load and store atomic isn't even sufficient,
|
||||||
|
because it's possible for complex invariants to exist between disjoint locations
|
||||||
|
in memory. For instance, the pointer and capacity of a Vec must be in sync.
|
||||||
|
|
||||||
|
Even concurrent paradigms that are traditionally regarded as Totally Safe like
|
||||||
|
message passing implicitly rely on some notion of thread safety -- are you
|
||||||
|
really message-passing if you pass a pointer? Send and Sync therefore require
|
||||||
|
some fundamental level of trust that Safe code can't provide, so they must be
|
||||||
|
unsafe to implement. To help obviate the pervasive unsafety that this would
|
||||||
|
introduce, Send (resp. Sync) is automatically derived for all types composed only
|
||||||
|
of Send (resp. Sync) values. 99% of types are Send and Sync, and 99% of those
|
||||||
|
never actually say it (the remaining 1% is overwhelmingly synchronization
|
||||||
|
primitives).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[drop flags]: drop-flags.html
|
||||||
|
[conversions]: conversions.html
|
80
src/doc/nomicon/send-and-sync.md
Normal file
80
src/doc/nomicon/send-and-sync.md
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
% Send and Sync
|
||||||
|
|
||||||
|
Not everything obeys inherited mutability, though. Some types allow you to
|
||||||
|
multiply alias a location in memory while mutating it. Unless these types use
|
||||||
|
synchronization to manage this access, they are absolutely not thread safe. Rust
|
||||||
|
captures this with through the `Send` and `Sync` traits.
|
||||||
|
|
||||||
|
* A type is Send if it is safe to send it to another thread.
|
||||||
|
* A type is Sync if it is safe to share between threads (`&T` is Send).
|
||||||
|
|
||||||
|
Send and Sync are fundamental to Rust's concurrency story. As such, a
|
||||||
|
substantial amount of special tooling exists to make them work right. First and
|
||||||
|
foremost, they're [unsafe traits][]. This means that they are unsafe to
|
||||||
|
implement, and other unsafe code can that they are correctly
|
||||||
|
implemented. Since they're *marker traits* (they have no associated items like
|
||||||
|
methods), correctly implemented simply means that they have the intrinsic
|
||||||
|
properties an implementor should have. Incorrectly implementing Send or Sync can
|
||||||
|
cause Undefined Behaviour.
|
||||||
|
|
||||||
|
Send and Sync are also automatically derived traits. This means that, unlike
|
||||||
|
every other trait, if a type is composed entirely of Send or Sync types, then it
|
||||||
|
is Send or Sync. Almost all primitives are Send and Sync, and as a consequence
|
||||||
|
pretty much all types you'll ever interact with are Send and Sync.
|
||||||
|
|
||||||
|
Major exceptions include:
|
||||||
|
|
||||||
|
* raw pointers are neither Send nor Sync (because they have no safety guards).
|
||||||
|
* `UnsafeCell` isn't Sync (and therefore `Cell` and `RefCell` aren't).
|
||||||
|
* `Rc` isn't Send or Sync (because the refcount is shared and unsynchronized).
|
||||||
|
|
||||||
|
`Rc` and `UnsafeCell` are very fundamentally not thread-safe: they enable
|
||||||
|
unsynchronized shared mutable state. However raw pointers are, strictly
|
||||||
|
speaking, marked as thread-unsafe as more of a *lint*. Doing anything useful
|
||||||
|
with a raw pointer requires dereferencing it, which is already unsafe. In that
|
||||||
|
sense, one could argue that it would be "fine" for them to be marked as thread
|
||||||
|
safe.
|
||||||
|
|
||||||
|
However it's important that they aren't thread safe to prevent types that
|
||||||
|
contain them from being automatically marked as thread safe. These types have
|
||||||
|
non-trivial untracked ownership, and it's unlikely that their author was
|
||||||
|
necessarily thinking hard about thread safety. In the case of Rc, we have a nice
|
||||||
|
example of a type that contains a `*mut` that is definitely not thread safe.
|
||||||
|
|
||||||
|
Types that aren't automatically derived can simply implement them if desired:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
struct MyBox(*mut u8);
|
||||||
|
|
||||||
|
unsafe impl Send for MyBox {}
|
||||||
|
unsafe impl Sync for MyBox {}
|
||||||
|
```
|
||||||
|
|
||||||
|
In the *incredibly rare* case that a type is inappropriately automatically
|
||||||
|
derived to be Send or Sync, then one can also unimplement Send and Sync:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
#![feature(optin_builtin_traits)]
|
||||||
|
|
||||||
|
// I have some magic semantics for some synchronization primitive!
|
||||||
|
struct SpecialThreadToken(u8);
|
||||||
|
|
||||||
|
impl !Send for SpecialThreadToken {}
|
||||||
|
impl !Sync for SpecialThreadToken {}
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that *in and of itself* it is impossible to incorrectly derive Send and
|
||||||
|
Sync. Only types that are ascribed special meaning by other unsafe code can
|
||||||
|
possible cause trouble by being incorrectly Send or Sync.
|
||||||
|
|
||||||
|
Most uses of raw pointers should be encapsulated behind a sufficient abstraction
|
||||||
|
that Send and Sync can be derived. For instance all of Rust's standard
|
||||||
|
collections are Send and Sync (when they contain Send and Sync types) in spite
|
||||||
|
of their pervasive use of raw pointers to manage allocations and complex ownership.
|
||||||
|
Similarly, most iterators into these collections are Send and Sync because they
|
||||||
|
largely behave like an `&` or `&mut` into the collection.
|
||||||
|
|
||||||
|
TODO: better explain what can or can't be Send or Sync. Sufficient to appeal
|
||||||
|
only to data races?
|
||||||
|
|
||||||
|
[unsafe traits]: safe-unsafe-meaning.html
|
212
src/doc/nomicon/subtyping.md
Normal file
212
src/doc/nomicon/subtyping.md
Normal file
@ -0,0 +1,212 @@
|
|||||||
|
% Subtyping and Variance
|
||||||
|
|
||||||
|
Although Rust doesn't have any notion of structural inheritance, it *does*
|
||||||
|
include subtyping. In Rust, subtyping derives entirely from lifetimes. Since
|
||||||
|
lifetimes are scopes, we can partially order them based on the *contains*
|
||||||
|
(outlives) relationship. We can even express this as a generic bound.
|
||||||
|
|
||||||
|
Subtyping on lifetimes is in terms of that relationship: if `'a: 'b` ("a contains
|
||||||
|
b" or "a outlives b"), then `'a` is a subtype of `'b`. This is a large source of
|
||||||
|
confusion, because it seems intuitively backwards to many: the bigger scope is a
|
||||||
|
*subtype* of the smaller scope.
|
||||||
|
|
||||||
|
This does in fact make sense, though. The intuitive reason for this is that if
|
||||||
|
you expect an `&'a u8`, then it's totally fine for me to hand you an `&'static
|
||||||
|
u8`, in the same way that if you expect an Animal in Java, it's totally fine for
|
||||||
|
me to hand you a Cat. Cats are just Animals *and more*, just as `'static` is
|
||||||
|
just `'a` *and more*.
|
||||||
|
|
||||||
|
(Note, the subtyping relationship and typed-ness of lifetimes is a fairly
|
||||||
|
arbitrary construct that some disagree with. However it simplifies our analysis
|
||||||
|
to treat lifetimes and types uniformly.)
|
||||||
|
|
||||||
|
Higher-ranked lifetimes are also subtypes of every concrete lifetime. This is
|
||||||
|
because taking an arbitrary lifetime is strictly more general than taking a
|
||||||
|
specific one.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Variance
|
||||||
|
|
||||||
|
Variance is where things get a bit complicated.
|
||||||
|
|
||||||
|
Variance is a property that *type constructors* have with respect to their
|
||||||
|
arguments. A type constructor in Rust is a generic type with unbound arguments.
|
||||||
|
For instance `Vec` is a type constructor that takes a `T` and returns a
|
||||||
|
`Vec<T>`. `&` and `&mut` are type constructors that take two inputs: a
|
||||||
|
lifetime, and a type to point to.
|
||||||
|
|
||||||
|
A type constructor's *variance* is how the subtyping of its inputs affects the
|
||||||
|
subtyping of its outputs. There are two kinds of variance in Rust:
|
||||||
|
|
||||||
|
* F is *variant* over `T` if `T` being a subtype of `U` implies
|
||||||
|
`F<T>` is a subtype of `F<U>` (subtyping "passes through")
|
||||||
|
* F is *invariant* over `T` otherwise (no subtyping relation can be derived)
|
||||||
|
|
||||||
|
(For those of you who are familiar with variance from other languages, what we
|
||||||
|
refer to as "just" variance is in fact *covariance*. Rust does not have
|
||||||
|
contravariance. Historically Rust did have some contravariance but it was
|
||||||
|
scrapped due to poor interactions with other features. If you experience
|
||||||
|
contravariance in Rust call your local compiler developer for medical advice.)
|
||||||
|
|
||||||
|
Some important variances:
|
||||||
|
|
||||||
|
* `&'a T` is variant over `'a` and `T` (as is `*const T` by metaphor)
|
||||||
|
* `&'a mut T` is variant with over `'a` but invariant over `T`
|
||||||
|
* `Fn(T) -> U` is invariant over `T`, but variant over `U`
|
||||||
|
* `Box`, `Vec`, and all other collections are variant over the types of
|
||||||
|
their contents
|
||||||
|
* `UnsafeCell<T>`, `Cell<T>`, `RefCell<T>`, `Mutex<T>` and all other
|
||||||
|
interior mutability types are invariant over T (as is `*mut T` by metaphor)
|
||||||
|
|
||||||
|
To understand why these variances are correct and desirable, we will consider
|
||||||
|
several examples.
|
||||||
|
|
||||||
|
|
||||||
|
We have already covered why `&'a T` should be variant over `'a` when
|
||||||
|
introducing subtyping: it's desirable to be able to pass longer-lived things
|
||||||
|
where shorter-lived things are needed.
|
||||||
|
|
||||||
|
Similar reasoning applies to why it should be variant over T. It is reasonable
|
||||||
|
to be able to pass `&&'static str` where an `&&'a str` is expected. The
|
||||||
|
additional level of indirection does not change the desire to be able to pass
|
||||||
|
longer lived things where shorted lived things are expected.
|
||||||
|
|
||||||
|
However this logic doesn't apply to `&mut`. To see why `&mut` should
|
||||||
|
be invariant over T, consider the following code:
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
fn overwrite<T: Copy>(input: &mut T, new: &mut T) {
|
||||||
|
*input = *new;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut forever_str: &'static str = "hello";
|
||||||
|
{
|
||||||
|
let string = String::from("world");
|
||||||
|
overwrite(&mut forever_str, &mut &*string);
|
||||||
|
}
|
||||||
|
// Oops, printing free'd memory
|
||||||
|
println!("{}", forever_str);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The signature of `overwrite` is clearly valid: it takes mutable references to
|
||||||
|
two values of the same type, and overwrites one with the other. If `&mut T` was
|
||||||
|
variant over T, then `&mut &'a str` would be a subtype of `&mut &'static str`,
|
||||||
|
since `&'a str` is a subtype of `&'static str`. Therefore the lifetime of
|
||||||
|
`forever_str` would successfully be "shrunk" down to the shorter lifetime of
|
||||||
|
`string`, and `overwrite` would be called successfully. `string` would
|
||||||
|
subsequently be dropped, and `forever_str` would point to freed memory when we
|
||||||
|
print it! Therefore `&mut` should be invariant.
|
||||||
|
|
||||||
|
This is the general theme of variance vs invariance: if variance would allow you
|
||||||
|
to store a short-lived value into a longer-lived slot, then you must be
|
||||||
|
invariant.
|
||||||
|
|
||||||
|
However it *is* sound for `&'a mut T` to be variant over `'a`. The key difference
|
||||||
|
between `'a` and T is that `'a` is a property of the reference itself,
|
||||||
|
while T is something the reference is borrowing. If you change T's type, then
|
||||||
|
the source still remembers the original type. However if you change the
|
||||||
|
lifetime's type, no one but the reference knows this information, so it's fine.
|
||||||
|
Put another way: `&'a mut T` owns `'a`, but only *borrows* T.
|
||||||
|
|
||||||
|
`Box` and `Vec` are interesting cases because they're variant, but you can
|
||||||
|
definitely store values in them! This is where Rust gets really clever: it's
|
||||||
|
fine for them to be variant because you can only store values
|
||||||
|
in them *via a mutable reference*! The mutable reference makes the whole type
|
||||||
|
invariant, and therefore prevents you from smuggling a short-lived type into
|
||||||
|
them.
|
||||||
|
|
||||||
|
Being variant allows `Box` and `Vec` to be weakened when shared
|
||||||
|
immutably. So you can pass a `&Box<&'static str>` where a `&Box<&'a str>` is
|
||||||
|
expected.
|
||||||
|
|
||||||
|
However what should happen when passing *by-value* is less obvious. It turns out
|
||||||
|
that, yes, you can use subtyping when passing by-value. That is, this works:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn get_box<'a>(str: &'a str) -> Box<&'a str> {
|
||||||
|
// string literals are `&'static str`s
|
||||||
|
Box::new("hello")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Weakening when you pass by-value is fine because there's no one else who
|
||||||
|
"remembers" the old lifetime in the Box. The reason a variant `&mut` was
|
||||||
|
trouble was because there's always someone else who remembers the original
|
||||||
|
subtype: the actual owner.
|
||||||
|
|
||||||
|
The invariance of the cell types can be seen as follows: `&` is like an `&mut`
|
||||||
|
for a cell, because you can still store values in them through an `&`. Therefore
|
||||||
|
cells must be invariant to avoid lifetime smuggling.
|
||||||
|
|
||||||
|
`Fn` is the most subtle case because it has mixed variance. To see why
|
||||||
|
`Fn(T) -> U` should be invariant over T, consider the following function
|
||||||
|
signature:
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
// 'a is derived from some parent scope
|
||||||
|
fn foo(&'a str) -> usize;
|
||||||
|
```
|
||||||
|
|
||||||
|
This signature claims that it can handle any `&str` that lives at least as
|
||||||
|
long as `'a`. Now if this signature was variant over `&'a str`, that
|
||||||
|
would mean
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
fn foo(&'static str) -> usize;
|
||||||
|
```
|
||||||
|
|
||||||
|
could be provided in its place, as it would be a subtype. However this function
|
||||||
|
has a stronger requirement: it says that it can only handle `&'static str`s,
|
||||||
|
and nothing else. Giving `&'a str`s to it would be unsound, as it's free to
|
||||||
|
assume that what it's given lives forever. Therefore functions are not variant
|
||||||
|
over their arguments.
|
||||||
|
|
||||||
|
To see why `Fn(T) -> U` should be variant over U, consider the following
|
||||||
|
function signature:
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
// 'a is derived from some parent scope
|
||||||
|
fn foo(usize) -> &'a str;
|
||||||
|
```
|
||||||
|
|
||||||
|
This signature claims that it will return something that outlives `'a`. It is
|
||||||
|
therefore completely reasonable to provide
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
fn foo(usize) -> &'static str;
|
||||||
|
```
|
||||||
|
|
||||||
|
in its place. Therefore functions are variant over their return type.
|
||||||
|
|
||||||
|
`*const` has the exact same semantics as `&`, so variance follows. `*mut` on the
|
||||||
|
other hand can dereference to an `&mut` whether shared or not, so it is marked
|
||||||
|
as invariant just like cells.
|
||||||
|
|
||||||
|
This is all well and good for the types the standard library provides, but
|
||||||
|
how is variance determined for type that *you* define? A struct, informally
|
||||||
|
speaking, inherits the variance of its fields. If a struct `Foo`
|
||||||
|
has a generic argument `A` that is used in a field `a`, then Foo's variance
|
||||||
|
over `A` is exactly `a`'s variance. However this is complicated if `A` is used
|
||||||
|
in multiple fields.
|
||||||
|
|
||||||
|
* If all uses of A are variant, then Foo is variant over A
|
||||||
|
* Otherwise, Foo is invariant over A
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use std::cell::Cell;
|
||||||
|
|
||||||
|
struct Foo<'a, 'b, A: 'a, B: 'b, C, D, E, F, G, H> {
|
||||||
|
a: &'a A, // variant over 'a and A
|
||||||
|
b: &'b mut B, // invariant over 'b and B
|
||||||
|
c: *const C, // variant over C
|
||||||
|
d: *mut D, // invariant over D
|
||||||
|
e: Vec<E>, // variant over E
|
||||||
|
f: Cell<F>, // invariant over F
|
||||||
|
g: G, // variant over G
|
||||||
|
h1: H, // would also be variant over H except...
|
||||||
|
h2: Cell<H>, // invariant over H, because invariance wins
|
||||||
|
}
|
||||||
|
```
|
35
src/doc/nomicon/transmutes.md
Normal file
35
src/doc/nomicon/transmutes.md
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
% Transmutes
|
||||||
|
|
||||||
|
Get out of our way type system! We're going to reinterpret these bits or die
|
||||||
|
trying! Even though this book is all about doing things that are unsafe, I
|
||||||
|
really can't emphasize that you should deeply think about finding Another Way
|
||||||
|
than the operations covered in this section. This is really, truly, the most
|
||||||
|
horribly unsafe thing you can do in Rust. The railguards here are dental floss.
|
||||||
|
|
||||||
|
`mem::transmute<T, U>` takes a value of type `T` and reinterprets it to have
|
||||||
|
type `U`. The only restriction is that the `T` and `U` are verified to have the
|
||||||
|
same size. The ways to cause Undefined Behaviour with this are mind boggling.
|
||||||
|
|
||||||
|
* First and foremost, creating an instance of *any* type with an invalid state
|
||||||
|
is going to cause arbitrary chaos that can't really be predicted.
|
||||||
|
* Transmute has an overloaded return type. If you do not specify the return type
|
||||||
|
it may produce a surprising type to satisfy inference.
|
||||||
|
* Making a primitive with an invalid value is UB
|
||||||
|
* Transmuting between non-repr(C) types is UB
|
||||||
|
* Transmuting an & to &mut is UB
|
||||||
|
* Transmuting an & to &mut is *always* UB
|
||||||
|
* No you can't do it
|
||||||
|
* No you're not special
|
||||||
|
* Transmuting to a reference without an explicitly provided lifetime
|
||||||
|
produces an [unbounded lifetime][]
|
||||||
|
|
||||||
|
`mem::transmute_copy<T, U>` somehow manages to be *even more* wildly unsafe than
|
||||||
|
this. It copies `size_of<U>` bytes out of an `&T` and interprets them as a `U`.
|
||||||
|
The size check that `mem::transmute` has is gone (as it may be valid to copy
|
||||||
|
out a prefix), though it is Undefined Behaviour for `U` to be larger than `T`.
|
||||||
|
|
||||||
|
Also of course you can get most of the functionality of these functions using
|
||||||
|
pointer casts.
|
||||||
|
|
||||||
|
|
||||||
|
[unbounded lifetime]: unbounded-lifetimes.html
|
37
src/doc/nomicon/unbounded-lifetimes.md
Normal file
37
src/doc/nomicon/unbounded-lifetimes.md
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
% Unbounded Lifetimes
|
||||||
|
|
||||||
|
Unsafe code can often end up producing references or lifetimes out of thin air.
|
||||||
|
Such lifetimes come into the world as *unbounded*. The most common source of this
|
||||||
|
is derefencing a raw pointer, which produces a reference with an unbounded lifetime.
|
||||||
|
Such a lifetime becomes as big as context demands. This is in fact more powerful
|
||||||
|
than simply becoming `'static`, because for instance `&'static &'a T`
|
||||||
|
will fail to typecheck, but the unbound lifetime will perfectly mold into
|
||||||
|
`&'a &'a T` as needed. However for most intents and purposes, such an unbounded
|
||||||
|
lifetime can be regarded as `'static`.
|
||||||
|
|
||||||
|
Almost no reference is `'static`, so this is probably wrong. `transmute` and
|
||||||
|
`transmute_copy` are the two other primary offenders. One should endeavour to
|
||||||
|
bound an unbounded lifetime as quick as possible, especially across function
|
||||||
|
boundaries.
|
||||||
|
|
||||||
|
Given a function, any output lifetimes that don't derive from inputs are
|
||||||
|
unbounded. For instance:
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
fn get_str<'a>() -> &'a str;
|
||||||
|
```
|
||||||
|
|
||||||
|
will produce an `&str` with an unbounded lifetime. The easiest way to avoid
|
||||||
|
unbounded lifetimes is to use lifetime elision at the function boundary.
|
||||||
|
If an output lifetime is elided, then it *must* be bounded by an input lifetime.
|
||||||
|
Of course it might be bounded by the *wrong* lifetime, but this will usually
|
||||||
|
just cause a compiler error, rather than allow memory safety to be trivially
|
||||||
|
violated.
|
||||||
|
|
||||||
|
Within a function, bounding lifetimes is more error-prone. The safest and easiest
|
||||||
|
way to bound a lifetime is to return it from a function with a bound lifetime.
|
||||||
|
However if this is unacceptable, the reference can be placed in a location with
|
||||||
|
a specific lifetime. Unfortunately it's impossible to name all lifetimes involved
|
||||||
|
in a function. To get around this, you can in principle use `copy_lifetime`, though
|
||||||
|
these are unstable due to their awkward nature and questionable utility.
|
||||||
|
|
85
src/doc/nomicon/unchecked-uninit.md
Normal file
85
src/doc/nomicon/unchecked-uninit.md
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
% Unchecked Uninitialized Memory
|
||||||
|
|
||||||
|
One interesting exception to this rule is working with arrays. Safe Rust doesn't
|
||||||
|
permit you to partially initialize an array. When you initialize an array, you
|
||||||
|
can either set every value to the same thing with `let x = [val; N]`, or you can
|
||||||
|
specify each member individually with `let x = [val1, val2, val3]`.
|
||||||
|
Unfortunately this is pretty rigid, especially if you need to initialize your
|
||||||
|
array in a more incremental or dynamic way.
|
||||||
|
|
||||||
|
Unsafe Rust gives us a powerful tool to handle this problem:
|
||||||
|
`mem::uninitialized`. This function pretends to return a value when really
|
||||||
|
it does nothing at all. Using it, we can convince Rust that we have initialized
|
||||||
|
a variable, allowing us to do trickier things with conditional and incremental
|
||||||
|
initialization.
|
||||||
|
|
||||||
|
Unfortunately, this opens us up to all kinds of problems. Assignment has a
|
||||||
|
different meaning to Rust based on whether it believes that a variable is
|
||||||
|
initialized or not. If it's believed uninitialized, then Rust will semantically
|
||||||
|
just memcopy the bits over the uninitialized ones, and do nothing else. However
|
||||||
|
if Rust believes a value to be initialized, it will try to `Drop` the old value!
|
||||||
|
Since we've tricked Rust into believing that the value is initialized, we can no
|
||||||
|
longer safely use normal assignment.
|
||||||
|
|
||||||
|
This is also a problem if you're working with a raw system allocator, which
|
||||||
|
returns a pointer to uninitialized memory.
|
||||||
|
|
||||||
|
To handle this, we must use the `ptr` module. In particular, it provides
|
||||||
|
three functions that allow us to assign bytes to a location in memory without
|
||||||
|
dropping the old value: `write`, `copy`, and `copy_nonoverlapping`.
|
||||||
|
|
||||||
|
* `ptr::write(ptr, val)` takes a `val` and moves it into the address pointed
|
||||||
|
to by `ptr`.
|
||||||
|
* `ptr::copy(src, dest, count)` copies the bits that `count` T's would occupy
|
||||||
|
from src to dest. (this is equivalent to memmove -- note that the argument
|
||||||
|
order is reversed!)
|
||||||
|
* `ptr::copy_nonoverlapping(src, dest, count)` does what `copy` does, but a
|
||||||
|
little faster on the assumption that the two ranges of memory don't overlap.
|
||||||
|
(this is equivalent to memcpy -- note that the argument order is reversed!)
|
||||||
|
|
||||||
|
It should go without saying that these functions, if misused, will cause serious
|
||||||
|
havoc or just straight up Undefined Behaviour. The only things that these
|
||||||
|
functions *themselves* require is that the locations you want to read and write
|
||||||
|
are allocated. However the ways writing arbitrary bits to arbitrary
|
||||||
|
locations of memory can break things are basically uncountable!
|
||||||
|
|
||||||
|
Putting this all together, we get the following:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use std::mem;
|
||||||
|
use std::ptr;
|
||||||
|
|
||||||
|
// size of the array is hard-coded but easy to change. This means we can't
|
||||||
|
// use [a, b, c] syntax to initialize the array, though!
|
||||||
|
const SIZE: usize = 10;
|
||||||
|
|
||||||
|
let mut x: [Box<u32>; SIZE];
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
// convince Rust that x is Totally Initialized
|
||||||
|
x = mem::uninitialized();
|
||||||
|
for i in 0..SIZE {
|
||||||
|
// very carefully overwrite each index without reading it
|
||||||
|
// NOTE: exception safety is not a concern; Box can't panic
|
||||||
|
ptr::write(&mut x[i], Box::new(i as u32));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("{:?}", x);
|
||||||
|
```
|
||||||
|
|
||||||
|
It's worth noting that you don't need to worry about `ptr::write`-style
|
||||||
|
shenanigans with types which don't implement `Drop` or contain `Drop` types,
|
||||||
|
because Rust knows not to try to drop them. Similarly you should be able to
|
||||||
|
assign to fields of partially initialized structs directly if those fields don't
|
||||||
|
contain any `Drop` types.
|
||||||
|
|
||||||
|
However when working with uninitialized memory you need to be ever-vigilant for
|
||||||
|
Rust trying to drop values you make like this before they're fully initialized.
|
||||||
|
Every control path through that variable's scope must initialize the value
|
||||||
|
before it ends, if has a destructor.
|
||||||
|
*[This includes code panicking](unwinding.html)*.
|
||||||
|
|
||||||
|
And that's about it for working with uninitialized memory! Basically nothing
|
||||||
|
anywhere expects to be handed uninitialized memory, so if you're going to pass
|
||||||
|
it around at all, be sure to be *really* careful.
|
10
src/doc/nomicon/uninitialized.md
Normal file
10
src/doc/nomicon/uninitialized.md
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
% Working With Uninitialized Memory
|
||||||
|
|
||||||
|
All runtime-allocated memory in a Rust program begins its life as
|
||||||
|
*uninitialized*. In this state the value of the memory is an indeterminate pile
|
||||||
|
of bits that may or may not even reflect a valid state for the type that is
|
||||||
|
supposed to inhabit that location of memory. Attempting to interpret this memory
|
||||||
|
as a value of *any* type will cause Undefined Behaviour. Do Not Do This.
|
||||||
|
|
||||||
|
Rust provides mechanisms to work with uninitialized memory in checked (safe) and
|
||||||
|
unchecked (unsafe) ways.
|
49
src/doc/nomicon/unwinding.md
Normal file
49
src/doc/nomicon/unwinding.md
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
% Unwinding
|
||||||
|
|
||||||
|
Rust has a *tiered* error-handling scheme:
|
||||||
|
|
||||||
|
* If something might reasonably be absent, Option is used.
|
||||||
|
* If something goes wrong and can reasonably be handled, Result is used.
|
||||||
|
* If something goes wrong and cannot reasonably be handled, the thread panics.
|
||||||
|
* If something catastrophic happens, the program aborts.
|
||||||
|
|
||||||
|
Option and Result are overwhelmingly preferred in most situations, especially
|
||||||
|
since they can be promoted into a panic or abort at the API user's discretion.
|
||||||
|
Panics cause the thread to halt normal execution and unwind its stack, calling
|
||||||
|
destructors as if every function instantly returned.
|
||||||
|
|
||||||
|
As of 1.0, Rust is of two minds when it comes to panics. In the long-long-ago,
|
||||||
|
Rust was much more like Erlang. Like Erlang, Rust had lightweight tasks,
|
||||||
|
and tasks were intended to kill themselves with a panic when they reached an
|
||||||
|
untenable state. Unlike an exception in Java or C++, a panic could not be
|
||||||
|
caught at any time. Panics could only be caught by the owner of the task, at which
|
||||||
|
point they had to be handled or *that* task would itself panic.
|
||||||
|
|
||||||
|
Unwinding was important to this story because if a task's
|
||||||
|
destructors weren't called, it would cause memory and other system resources to
|
||||||
|
leak. Since tasks were expected to die during normal execution, this would make
|
||||||
|
Rust very poor for long-running systems!
|
||||||
|
|
||||||
|
As the Rust we know today came to be, this style of programming grew out of
|
||||||
|
fashion in the push for less-and-less abstraction. Light-weight tasks were
|
||||||
|
killed in the name of heavy-weight OS threads. Still, on stable Rust as of 1.0
|
||||||
|
panics can only be caught by the parent thread. This means catching a panic
|
||||||
|
requires spinning up an entire OS thread! This unfortunately stands in conflict
|
||||||
|
to Rust's philosophy of zero-cost abstractions.
|
||||||
|
|
||||||
|
There is an unstable API called `catch_panic` that enables catching a panic
|
||||||
|
without spawning a thread. Still, we would encourage you to only do this
|
||||||
|
sparingly. In particular, Rust's current unwinding implementation is heavily
|
||||||
|
optimized for the "doesn't unwind" case. If a program doesn't unwind, there
|
||||||
|
should be no runtime cost for the program being *ready* to unwind. As a
|
||||||
|
consequence, actually unwinding will be more expensive than in e.g. Java.
|
||||||
|
Don't build your programs to unwind under normal circumstances. Ideally, you
|
||||||
|
should only panic for programming errors or *extreme* problems.
|
||||||
|
|
||||||
|
Rust's unwinding strategy is not specified to be fundamentally compatible
|
||||||
|
with any other language's unwinding. As such, unwinding into Rust from another
|
||||||
|
language, or unwinding into another language from Rust is Undefined Behaviour.
|
||||||
|
You must *absolutely* catch any panics at the FFI boundary! What you do at that
|
||||||
|
point is up to you, but *something* must be done. If you fail to do this,
|
||||||
|
at best, your application will crash and burn. At worst, your application *won't*
|
||||||
|
crash and burn, and will proceed with completely clobbered state.
|
222
src/doc/nomicon/vec-alloc.md
Normal file
222
src/doc/nomicon/vec-alloc.md
Normal file
@ -0,0 +1,222 @@
|
|||||||
|
% Allocating Memory
|
||||||
|
|
||||||
|
Using Unique throws a wrench in an important feature of Vec (and indeed all of
|
||||||
|
the std collections): an empty Vec doesn't actually allocate at all. So if we
|
||||||
|
can't allocate, but also can't put a null pointer in `ptr`, what do we do in
|
||||||
|
`Vec::new`? Well, we just put some other garbage in there!
|
||||||
|
|
||||||
|
This is perfectly fine because we already have `cap == 0` as our sentinel for no
|
||||||
|
allocation. We don't even need to handle it specially in almost any code because
|
||||||
|
we usually need to check if `cap > len` or `len > 0` anyway. The traditional
|
||||||
|
Rust value to put here is `0x01`. The standard library actually exposes this
|
||||||
|
as `std::rt::heap::EMPTY`. There are quite a few places where we'll
|
||||||
|
want to use `heap::EMPTY` because there's no real allocation to talk about but
|
||||||
|
`null` would make the compiler do bad things.
|
||||||
|
|
||||||
|
All of the `heap` API is totally unstable under the `heap_api` feature, though.
|
||||||
|
We could trivially define `heap::EMPTY` ourselves, but we'll want the rest of
|
||||||
|
the `heap` API anyway, so let's just get that dependency over with.
|
||||||
|
|
||||||
|
So:
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
#![feature(heap_api)]
|
||||||
|
|
||||||
|
use std::rt::heap::EMPTY;
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
|
impl<T> Vec<T> {
|
||||||
|
fn new() -> Self {
|
||||||
|
assert!(mem::size_of::<T>() != 0, "We're not ready to handle ZSTs");
|
||||||
|
unsafe {
|
||||||
|
// need to cast EMPTY to the actual ptr type we want, let
|
||||||
|
// inference handle it.
|
||||||
|
Vec { ptr: Unique::new(heap::EMPTY as *mut _), len: 0, cap: 0 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
I slipped in that assert there because zero-sized types will require some
|
||||||
|
special handling throughout our code, and I want to defer the issue for now.
|
||||||
|
Without this assert, some of our early drafts will do some Very Bad Things.
|
||||||
|
|
||||||
|
Next we need to figure out what to actually do when we *do* want space. For
|
||||||
|
that, we'll need to use the rest of the heap APIs. These basically allow us to
|
||||||
|
talk directly to Rust's allocator (jemalloc by default).
|
||||||
|
|
||||||
|
We'll also need a way to handle out-of-memory (OOM) conditions. The standard
|
||||||
|
library calls the `abort` intrinsic, which just calls an illegal instruction to
|
||||||
|
crash the whole program. The reason we abort and don't panic is because
|
||||||
|
unwinding can cause allocations to happen, and that seems like a bad thing to do
|
||||||
|
when your allocator just came back with "hey I don't have any more memory".
|
||||||
|
|
||||||
|
Of course, this is a bit silly since most platforms don't actually run out of
|
||||||
|
memory in a conventional way. Your operating system will probably kill the
|
||||||
|
application by another means if you legitimately start using up all the memory.
|
||||||
|
The most likely way we'll trigger OOM is by just asking for ludicrous quantities
|
||||||
|
of memory at once (e.g. half the theoretical address space). As such it's
|
||||||
|
*probably* fine to panic and nothing bad will happen. Still, we're trying to be
|
||||||
|
like the standard library as much as possible, so we'll just kill the whole
|
||||||
|
program.
|
||||||
|
|
||||||
|
We said we don't want to use intrinsics, so doing exactly what `std` does is
|
||||||
|
out. Instead, we'll call `std::process::exit` with some random number.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn oom() {
|
||||||
|
::std::process::exit(-9999);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Okay, now we can write growing. Roughly, we want to have this logic:
|
||||||
|
|
||||||
|
```text
|
||||||
|
if cap == 0:
|
||||||
|
allocate()
|
||||||
|
cap = 1
|
||||||
|
else:
|
||||||
|
reallocate()
|
||||||
|
cap *= 2
|
||||||
|
```
|
||||||
|
|
||||||
|
But Rust's only supported allocator API is so low level that we'll need to do a
|
||||||
|
fair bit of extra work. We also need to guard against some special
|
||||||
|
conditions that can occur with really large allocations or empty allocations.
|
||||||
|
|
||||||
|
In particular, `ptr::offset` will cause us a lot of trouble, because it has
|
||||||
|
the semantics of LLVM's GEP inbounds instruction. If you're fortunate enough to
|
||||||
|
not have dealt with this instruction, here's the basic story with GEP: alias
|
||||||
|
analysis, alias analysis, alias analysis. It's super important to an optimizing
|
||||||
|
compiler to be able to reason about data dependencies and aliasing.
|
||||||
|
|
||||||
|
As a simple example, consider the following fragment of code:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
# let x = &mut 0;
|
||||||
|
# let y = &mut 0;
|
||||||
|
*x *= 7;
|
||||||
|
*y *= 3;
|
||||||
|
```
|
||||||
|
|
||||||
|
If the compiler can prove that `x` and `y` point to different locations in
|
||||||
|
memory, the two operations can in theory be executed in parallel (by e.g.
|
||||||
|
loading them into different registers and working on them independently).
|
||||||
|
However the compiler can't do this in general because if x and y point to
|
||||||
|
the same location in memory, the operations need to be done to the same value,
|
||||||
|
and they can't just be merged afterwards.
|
||||||
|
|
||||||
|
When you use GEP inbounds, you are specifically telling LLVM that the offsets
|
||||||
|
you're about to do are within the bounds of a single "allocated" entity. The
|
||||||
|
ultimate payoff being that LLVM can assume that if two pointers are known to
|
||||||
|
point to two disjoint objects, all the offsets of those pointers are *also*
|
||||||
|
known to not alias (because you won't just end up in some random place in
|
||||||
|
memory). LLVM is heavily optimized to work with GEP offsets, and inbounds
|
||||||
|
offsets are the best of all, so it's important that we use them as much as
|
||||||
|
possible.
|
||||||
|
|
||||||
|
So that's what GEP's about, how can it cause us trouble?
|
||||||
|
|
||||||
|
The first problem is that we index into arrays with unsigned integers, but
|
||||||
|
GEP (and as a consequence `ptr::offset`) takes a signed integer. This means
|
||||||
|
that half of the seemingly valid indices into an array will overflow GEP and
|
||||||
|
actually go in the wrong direction! As such we must limit all allocations to
|
||||||
|
`isize::MAX` elements. This actually means we only need to worry about
|
||||||
|
byte-sized objects, because e.g. `> isize::MAX` `u16`s will truly exhaust all of
|
||||||
|
the system's memory. However in order to avoid subtle corner cases where someone
|
||||||
|
reinterprets some array of `< isize::MAX` objects as bytes, std limits all
|
||||||
|
allocations to `isize::MAX` bytes.
|
||||||
|
|
||||||
|
On all 64-bit targets that Rust currently supports we're artificially limited
|
||||||
|
to significantly less than all 64 bits of the address space (modern x64
|
||||||
|
platforms only expose 48-bit addressing), so we can rely on just running out of
|
||||||
|
memory first. However on 32-bit targets, particularly those with extensions to
|
||||||
|
use more of the address space (PAE x86 or x32), it's theoretically possible to
|
||||||
|
successfully allocate more than `isize::MAX` bytes of memory.
|
||||||
|
|
||||||
|
However since this is a tutorial, we're not going to be particularly optimal
|
||||||
|
here, and just unconditionally check, rather than use clever platform-specific
|
||||||
|
`cfg`s.
|
||||||
|
|
||||||
|
The other corner-case we need to worry about is empty allocations. There will
|
||||||
|
be two kinds of empty allocations we need to worry about: `cap = 0` for all T,
|
||||||
|
and `cap > 0` for zero-sized types.
|
||||||
|
|
||||||
|
These cases are tricky because they come
|
||||||
|
down to what LLVM means by "allocated". LLVM's notion of an
|
||||||
|
allocation is significantly more abstract than how we usually use it. Because
|
||||||
|
LLVM needs to work with different languages' semantics and custom allocators,
|
||||||
|
it can't really intimately understand allocation. Instead, the main idea behind
|
||||||
|
allocation is "doesn't overlap with other stuff". That is, heap allocations,
|
||||||
|
stack allocations, and globals don't randomly overlap. Yep, it's about alias
|
||||||
|
analysis. As such, Rust can technically play a bit fast an loose with the notion of
|
||||||
|
an allocation as long as it's *consistent*.
|
||||||
|
|
||||||
|
Getting back to the empty allocation case, there are a couple of places where
|
||||||
|
we want to offset by 0 as a consequence of generic code. The question is then:
|
||||||
|
is it consistent to do so? For zero-sized types, we have concluded that it is
|
||||||
|
indeed consistent to do a GEP inbounds offset by an arbitrary number of
|
||||||
|
elements. This is a runtime no-op because every element takes up no space,
|
||||||
|
and it's fine to pretend that there's infinite zero-sized types allocated
|
||||||
|
at `0x01`. No allocator will ever allocate that address, because they won't
|
||||||
|
allocate `0x00` and they generally allocate to some minimal alignment higher
|
||||||
|
than a byte. Also generally the whole first page of memory is
|
||||||
|
protected from being allocated anyway (a whole 4k, on many platforms).
|
||||||
|
|
||||||
|
However what about for positive-sized types? That one's a bit trickier. In
|
||||||
|
principle, you can argue that offsetting by 0 gives LLVM no information: either
|
||||||
|
there's an element before the address or after it, but it can't know which.
|
||||||
|
However we've chosen to conservatively assume that it may do bad things. As
|
||||||
|
such we will guard against this case explicitly.
|
||||||
|
|
||||||
|
*Phew*
|
||||||
|
|
||||||
|
Ok with all the nonsense out of the way, let's actually allocate some memory:
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
fn grow(&mut self) {
|
||||||
|
// this is all pretty delicate, so let's say it's all unsafe
|
||||||
|
unsafe {
|
||||||
|
// current API requires us to specify size and alignment manually.
|
||||||
|
let align = mem::align_of::<T>();
|
||||||
|
let elem_size = mem::size_of::<T>();
|
||||||
|
|
||||||
|
let (new_cap, ptr) = if self.cap == 0 {
|
||||||
|
let ptr = heap::allocate(elem_size, align);
|
||||||
|
(1, ptr)
|
||||||
|
} else {
|
||||||
|
// as an invariant, we can assume that `self.cap < isize::MAX`,
|
||||||
|
// so this doesn't need to be checked.
|
||||||
|
let new_cap = self.cap * 2;
|
||||||
|
// Similarly this can't overflow due to previously allocating this
|
||||||
|
let old_num_bytes = self.cap * elem_size;
|
||||||
|
|
||||||
|
// check that the new allocation doesn't exceed `isize::MAX` at all
|
||||||
|
// regardless of the actual size of the capacity. This combines the
|
||||||
|
// `new_cap <= isize::MAX` and `new_num_bytes <= usize::MAX` checks
|
||||||
|
// we need to make. We lose the ability to allocate e.g. 2/3rds of
|
||||||
|
// the address space with a single Vec of i16's on 32-bit though.
|
||||||
|
// Alas, poor Yorick -- I knew him, Horatio.
|
||||||
|
assert!(old_num_bytes <= (::std::isize::MAX as usize) / 2,
|
||||||
|
"capacity overflow");
|
||||||
|
|
||||||
|
let new_num_bytes = old_num_bytes * 2;
|
||||||
|
let ptr = heap::reallocate(*self.ptr as *mut _,
|
||||||
|
old_num_bytes,
|
||||||
|
new_num_bytes,
|
||||||
|
align);
|
||||||
|
(new_cap, ptr)
|
||||||
|
};
|
||||||
|
|
||||||
|
// If allocate or reallocate fail, we'll get `null` back
|
||||||
|
if ptr.is_null() { oom(); }
|
||||||
|
|
||||||
|
self.ptr = Unique::new(ptr as *mut _);
|
||||||
|
self.cap = new_cap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Nothing particularly tricky here. Just computing sizes and alignments and doing
|
||||||
|
some careful multiplication checks.
|
||||||
|
|
29
src/doc/nomicon/vec-dealloc.md
Normal file
29
src/doc/nomicon/vec-dealloc.md
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
% Deallocating
|
||||||
|
|
||||||
|
Next we should implement Drop so that we don't massively leak tons of resources.
|
||||||
|
The easiest way is to just call `pop` until it yields None, and then deallocate
|
||||||
|
our buffer. Note that calling `pop` is unneeded if `T: !Drop`. In theory we can
|
||||||
|
ask Rust if `T` `needs_drop` and omit the calls to `pop`. However in practice
|
||||||
|
LLVM is *really* good at removing simple side-effect free code like this, so I
|
||||||
|
wouldn't bother unless you notice it's not being stripped (in this case it is).
|
||||||
|
|
||||||
|
We must not call `heap::deallocate` when `self.cap == 0`, as in this case we
|
||||||
|
haven't actually allocated any memory.
|
||||||
|
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
impl<T> Drop for Vec<T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if self.cap != 0 {
|
||||||
|
while let Some(_) = self.pop() { }
|
||||||
|
|
||||||
|
let align = mem::align_of::<T>();
|
||||||
|
let elem_size = mem::size_of::<T>();
|
||||||
|
let num_bytes = elem_size * self.cap;
|
||||||
|
unsafe {
|
||||||
|
heap::deallocate(*self.ptr, num_bytes, align);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
42
src/doc/nomicon/vec-deref.md
Normal file
42
src/doc/nomicon/vec-deref.md
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
% Deref
|
||||||
|
|
||||||
|
Alright! We've got a decent minimal stack implemented. We can push, we can
|
||||||
|
pop, and we can clean up after ourselves. However there's a whole mess of
|
||||||
|
functionality we'd reasonably want. In particular, we have a proper array, but
|
||||||
|
none of the slice functionality. That's actually pretty easy to solve: we can
|
||||||
|
implement `Deref<Target=[T]>`. This will magically make our Vec coerce to, and
|
||||||
|
behave like, a slice in all sorts of conditions.
|
||||||
|
|
||||||
|
All we need is `slice::from_raw_parts`. It will correctly handle empty slices
|
||||||
|
for us. Later once we set up zero-sized type support it will also Just Work
|
||||||
|
for those too.
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
use std::ops::Deref;
|
||||||
|
|
||||||
|
impl<T> Deref for Vec<T> {
|
||||||
|
type Target = [T];
|
||||||
|
fn deref(&self) -> &[T] {
|
||||||
|
unsafe {
|
||||||
|
::std::slice::from_raw_parts(*self.ptr, self.len)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
And let's do DerefMut too:
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
use std::ops::DerefMut;
|
||||||
|
|
||||||
|
impl<T> DerefMut for Vec<T> {
|
||||||
|
fn deref_mut(&mut self) -> &mut [T] {
|
||||||
|
unsafe {
|
||||||
|
::std::slice::from_raw_parts_mut(*self.ptr, self.len)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Now we have `len`, `first`, `last`, indexing, slicing, sorting, `iter`,
|
||||||
|
`iter_mut`, and all other sorts of bells and whistles provided by slice. Sweet!
|
150
src/doc/nomicon/vec-drain.md
Normal file
150
src/doc/nomicon/vec-drain.md
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
% Drain
|
||||||
|
|
||||||
|
Let's move on to Drain. Drain is largely the same as IntoIter, except that
|
||||||
|
instead of consuming the Vec, it borrows the Vec and leaves its allocation
|
||||||
|
untouched. For now we'll only implement the "basic" full-range version.
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
|
struct Drain<'a, T: 'a> {
|
||||||
|
// Need to bound the lifetime here, so we do it with `&'a mut Vec<T>`
|
||||||
|
// because that's semantically what we contain. We're "just" calling
|
||||||
|
// `pop()` and `remove(0)`.
|
||||||
|
vec: PhantomData<&'a mut Vec<T>>
|
||||||
|
start: *const T,
|
||||||
|
end: *const T,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T> Iterator for Drain<'a, T> {
|
||||||
|
type Item = T;
|
||||||
|
fn next(&mut self) -> Option<T> {
|
||||||
|
if self.start == self.end {
|
||||||
|
None
|
||||||
|
```
|
||||||
|
|
||||||
|
-- wait, this is seeming familiar. Let's do some more compression. Both
|
||||||
|
IntoIter and Drain have the exact same structure, let's just factor it out.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
struct RawValIter<T> {
|
||||||
|
start: *const T,
|
||||||
|
end: *const T,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> RawValIter<T> {
|
||||||
|
// unsafe to construct because it has no associated lifetimes.
|
||||||
|
// This is necessary to store a RawValIter in the same struct as
|
||||||
|
// its actual allocation. OK since it's a private implementation
|
||||||
|
// detail.
|
||||||
|
unsafe fn new(slice: &[T]) -> Self {
|
||||||
|
RawValIter {
|
||||||
|
start: slice.as_ptr(),
|
||||||
|
end: if slice.len() == 0 {
|
||||||
|
// if `len = 0`, then this is not actually allocated memory.
|
||||||
|
// Need to avoid offsetting because that will give wrong
|
||||||
|
// information to LLVM via GEP.
|
||||||
|
slice.as_ptr()
|
||||||
|
} else {
|
||||||
|
slice.as_ptr().offset(slice.len() as isize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterator and DoubleEndedIterator impls identical to IntoIter.
|
||||||
|
```
|
||||||
|
|
||||||
|
And IntoIter becomes the following:
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
pub struct IntoIter<T> {
|
||||||
|
_buf: RawVec<T>, // we don't actually care about this. Just need it to live.
|
||||||
|
iter: RawValIter<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Iterator for IntoIter<T> {
|
||||||
|
type Item = T;
|
||||||
|
fn next(&mut self) -> Option<T> { self.iter.next() }
|
||||||
|
fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> DoubleEndedIterator for IntoIter<T> {
|
||||||
|
fn next_back(&mut self) -> Option<T> { self.iter.next_back() }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Drop for IntoIter<T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
for _ in &mut self.iter {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Vec<T> {
|
||||||
|
pub fn into_iter(self) -> IntoIter<T> {
|
||||||
|
unsafe {
|
||||||
|
let iter = RawValIter::new(&self);
|
||||||
|
|
||||||
|
let buf = ptr::read(&self.buf);
|
||||||
|
mem::forget(self);
|
||||||
|
|
||||||
|
IntoIter {
|
||||||
|
iter: iter,
|
||||||
|
_buf: buf,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that I've left a few quirks in this design to make upgrading Drain to work
|
||||||
|
with arbitrary subranges a bit easier. In particular we *could* have RawValIter
|
||||||
|
drain itself on drop, but that won't work right for a more complex Drain.
|
||||||
|
We also take a slice to simplify Drain initialization.
|
||||||
|
|
||||||
|
Alright, now Drain is really easy:
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
|
pub struct Drain<'a, T: 'a> {
|
||||||
|
vec: PhantomData<&'a mut Vec<T>>,
|
||||||
|
iter: RawValIter<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T> Iterator for Drain<'a, T> {
|
||||||
|
type Item = T;
|
||||||
|
fn next(&mut self) -> Option<T> { self.iter.next() }
|
||||||
|
fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T> DoubleEndedIterator for Drain<'a, T> {
|
||||||
|
fn next_back(&mut self) -> Option<T> { self.iter.next_back() }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T> Drop for Drain<'a, T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
for _ in &mut self.iter {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Vec<T> {
|
||||||
|
pub fn drain(&mut self) -> Drain<T> {
|
||||||
|
// this is a mem::forget safety thing. If Drain is forgotten, we just
|
||||||
|
// leak the whole Vec's contents. Also we need to do this eventually
|
||||||
|
// anyway, so why not do it now?
|
||||||
|
self.len = 0;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
Drain {
|
||||||
|
iter: RawValIter::new(&self),
|
||||||
|
vec: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
For more details on the `mem::forget` problem, see the
|
||||||
|
[section on leaks][leaks].
|
||||||
|
|
||||||
|
[leaks]: leaking.html
|
311
src/doc/nomicon/vec-final.md
Normal file
311
src/doc/nomicon/vec-final.md
Normal file
@ -0,0 +1,311 @@
|
|||||||
|
% The Final Code
|
||||||
|
|
||||||
|
```rust
|
||||||
|
#![feature(unique)]
|
||||||
|
#![feature(heap_api)]
|
||||||
|
|
||||||
|
use std::ptr::{Unique, self};
|
||||||
|
use std::rt::heap;
|
||||||
|
use std::mem;
|
||||||
|
use std::ops::{Deref, DerefMut};
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
struct RawVec<T> {
|
||||||
|
ptr: Unique<T>,
|
||||||
|
cap: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> RawVec<T> {
|
||||||
|
fn new() -> Self {
|
||||||
|
unsafe {
|
||||||
|
// !0 is usize::MAX. This branch should be stripped at compile time.
|
||||||
|
let cap = if mem::size_of::<T>() == 0 { !0 } else { 0 };
|
||||||
|
|
||||||
|
// heap::EMPTY doubles as "unallocated" and "zero-sized allocation"
|
||||||
|
RawVec { ptr: Unique::new(heap::EMPTY as *mut T), cap: cap }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn grow(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
let elem_size = mem::size_of::<T>();
|
||||||
|
|
||||||
|
// since we set the capacity to usize::MAX when elem_size is
|
||||||
|
// 0, getting to here necessarily means the Vec is overfull.
|
||||||
|
assert!(elem_size != 0, "capacity overflow");
|
||||||
|
|
||||||
|
let align = mem::align_of::<T>();
|
||||||
|
|
||||||
|
let (new_cap, ptr) = if self.cap == 0 {
|
||||||
|
let ptr = heap::allocate(elem_size, align);
|
||||||
|
(1, ptr)
|
||||||
|
} else {
|
||||||
|
let new_cap = 2 * self.cap;
|
||||||
|
let ptr = heap::reallocate(*self.ptr as *mut _,
|
||||||
|
self.cap * elem_size,
|
||||||
|
new_cap * elem_size,
|
||||||
|
align);
|
||||||
|
(new_cap, ptr)
|
||||||
|
};
|
||||||
|
|
||||||
|
// If allocate or reallocate fail, we'll get `null` back
|
||||||
|
if ptr.is_null() { oom() }
|
||||||
|
|
||||||
|
self.ptr = Unique::new(ptr as *mut _);
|
||||||
|
self.cap = new_cap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Drop for RawVec<T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
let elem_size = mem::size_of::<T>();
|
||||||
|
if self.cap != 0 && elem_size != 0 {
|
||||||
|
let align = mem::align_of::<T>();
|
||||||
|
|
||||||
|
let num_bytes = elem_size * self.cap;
|
||||||
|
unsafe {
|
||||||
|
heap::deallocate(*self.ptr as *mut _, num_bytes, align);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
pub struct Vec<T> {
|
||||||
|
buf: RawVec<T>,
|
||||||
|
len: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Vec<T> {
|
||||||
|
fn ptr(&self) -> *mut T { *self.buf.ptr }
|
||||||
|
|
||||||
|
fn cap(&self) -> usize { self.buf.cap }
|
||||||
|
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Vec { buf: RawVec::new(), len: 0 }
|
||||||
|
}
|
||||||
|
pub fn push(&mut self, elem: T) {
|
||||||
|
if self.len == self.cap() { self.buf.grow(); }
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
ptr::write(self.ptr().offset(self.len as isize), elem);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Can't fail, we'll OOM first.
|
||||||
|
self.len += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pop(&mut self) -> Option<T> {
|
||||||
|
if self.len == 0 {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
self.len -= 1;
|
||||||
|
unsafe {
|
||||||
|
Some(ptr::read(self.ptr().offset(self.len as isize)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert(&mut self, index: usize, elem: T) {
|
||||||
|
assert!(index <= self.len, "index out of bounds");
|
||||||
|
if self.cap() == self.len { self.buf.grow(); }
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
if index < self.len {
|
||||||
|
ptr::copy(self.ptr().offset(index as isize),
|
||||||
|
self.ptr().offset(index as isize + 1),
|
||||||
|
self.len - index);
|
||||||
|
}
|
||||||
|
ptr::write(self.ptr().offset(index as isize), elem);
|
||||||
|
self.len += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove(&mut self, index: usize) -> T {
|
||||||
|
assert!(index < self.len, "index out of bounds");
|
||||||
|
unsafe {
|
||||||
|
self.len -= 1;
|
||||||
|
let result = ptr::read(self.ptr().offset(index as isize));
|
||||||
|
ptr::copy(self.ptr().offset(index as isize + 1),
|
||||||
|
self.ptr().offset(index as isize),
|
||||||
|
self.len - index);
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_iter(self) -> IntoIter<T> {
|
||||||
|
unsafe {
|
||||||
|
let iter = RawValIter::new(&self);
|
||||||
|
let buf = ptr::read(&self.buf);
|
||||||
|
mem::forget(self);
|
||||||
|
|
||||||
|
IntoIter {
|
||||||
|
iter: iter,
|
||||||
|
_buf: buf,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn drain(&mut self) -> Drain<T> {
|
||||||
|
// this is a mem::forget safety thing. If this is forgotten, we just
|
||||||
|
// leak the whole Vec's contents. Also we need to do this *eventually*
|
||||||
|
// anyway, so why not do it now?
|
||||||
|
self.len = 0;
|
||||||
|
unsafe {
|
||||||
|
Drain {
|
||||||
|
iter: RawValIter::new(&self),
|
||||||
|
vec: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Drop for Vec<T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
while let Some(_) = self.pop() {}
|
||||||
|
// allocation is handled by RawVec
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Deref for Vec<T> {
|
||||||
|
type Target = [T];
|
||||||
|
fn deref(&self) -> &[T] {
|
||||||
|
unsafe {
|
||||||
|
::std::slice::from_raw_parts(self.ptr(), self.len)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> DerefMut for Vec<T> {
|
||||||
|
fn deref_mut(&mut self) -> &mut [T] {
|
||||||
|
unsafe {
|
||||||
|
::std::slice::from_raw_parts_mut(self.ptr(), self.len)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
struct RawValIter<T> {
|
||||||
|
start: *const T,
|
||||||
|
end: *const T,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> RawValIter<T> {
|
||||||
|
unsafe fn new(slice: &[T]) -> Self {
|
||||||
|
RawValIter {
|
||||||
|
start: slice.as_ptr(),
|
||||||
|
end: if mem::size_of::<T>() == 0 {
|
||||||
|
((slice.as_ptr() as usize) + slice.len()) as *const _
|
||||||
|
} else if slice.len() == 0 {
|
||||||
|
slice.as_ptr()
|
||||||
|
} else {
|
||||||
|
slice.as_ptr().offset(slice.len() as isize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Iterator for RawValIter<T> {
|
||||||
|
type Item = T;
|
||||||
|
fn next(&mut self) -> Option<T> {
|
||||||
|
if self.start == self.end {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
unsafe {
|
||||||
|
let result = ptr::read(self.start);
|
||||||
|
self.start = self.start.offset(1);
|
||||||
|
Some(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||||
|
let elem_size = mem::size_of::<T>();
|
||||||
|
let len = (self.end as usize - self.start as usize)
|
||||||
|
/ if elem_size == 0 { 1 } else { elem_size };
|
||||||
|
(len, Some(len))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> DoubleEndedIterator for RawValIter<T> {
|
||||||
|
fn next_back(&mut self) -> Option<T> {
|
||||||
|
if self.start == self.end {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
unsafe {
|
||||||
|
self.end = self.end.offset(-1);
|
||||||
|
Some(ptr::read(self.end))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
pub struct IntoIter<T> {
|
||||||
|
_buf: RawVec<T>, // we don't actually care about this. Just need it to live.
|
||||||
|
iter: RawValIter<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Iterator for IntoIter<T> {
|
||||||
|
type Item = T;
|
||||||
|
fn next(&mut self) -> Option<T> { self.iter.next() }
|
||||||
|
fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> DoubleEndedIterator for IntoIter<T> {
|
||||||
|
fn next_back(&mut self) -> Option<T> { self.iter.next_back() }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Drop for IntoIter<T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
for _ in &mut *self {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
pub struct Drain<'a, T: 'a> {
|
||||||
|
vec: PhantomData<&'a mut Vec<T>>,
|
||||||
|
iter: RawValIter<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T> Iterator for Drain<'a, T> {
|
||||||
|
type Item = T;
|
||||||
|
fn next(&mut self) -> Option<T> { self.iter.next_back() }
|
||||||
|
fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T> DoubleEndedIterator for Drain<'a, T> {
|
||||||
|
fn next_back(&mut self) -> Option<T> { self.iter.next_back() }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T> Drop for Drain<'a, T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
// pre-drain the iter
|
||||||
|
for _ in &mut self.iter {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Abort the process, we're out of memory!
|
||||||
|
///
|
||||||
|
/// In practice this is probably dead code on most OSes
|
||||||
|
fn oom() {
|
||||||
|
::std::process::exit(-9999);
|
||||||
|
}
|
||||||
|
|
||||||
|
# fn main() {}
|
||||||
|
```
|
51
src/doc/nomicon/vec-insert-remove.md
Normal file
51
src/doc/nomicon/vec-insert-remove.md
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
% Insert and Remove
|
||||||
|
|
||||||
|
Something *not* provided by slice is `insert` and `remove`, so let's do those
|
||||||
|
next.
|
||||||
|
|
||||||
|
Insert needs to shift all the elements at the target index to the right by one.
|
||||||
|
To do this we need to use `ptr::copy`, which is our version of C's `memmove`.
|
||||||
|
This copies some chunk of memory from one location to another, correctly
|
||||||
|
handling the case where the source and destination overlap (which will
|
||||||
|
definitely happen here).
|
||||||
|
|
||||||
|
If we insert at index `i`, we want to shift the `[i .. len]` to `[i+1 .. len+1]`
|
||||||
|
using the old len.
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
pub fn insert(&mut self, index: usize, elem: T) {
|
||||||
|
// Note: `<=` because it's valid to insert after everything
|
||||||
|
// which would be equivalent to push.
|
||||||
|
assert!(index <= self.len, "index out of bounds");
|
||||||
|
if self.cap == self.len { self.grow(); }
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
if index < self.len {
|
||||||
|
// ptr::copy(src, dest, len): "copy from source to dest len elems"
|
||||||
|
ptr::copy(self.ptr.offset(index as isize),
|
||||||
|
self.ptr.offset(index as isize + 1),
|
||||||
|
len - index);
|
||||||
|
}
|
||||||
|
ptr::write(self.ptr.offset(index as isize), elem);
|
||||||
|
self.len += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Remove behaves in the opposite manner. We need to shift all the elements from
|
||||||
|
`[i+1 .. len + 1]` to `[i .. len]` using the *new* len.
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
pub fn remove(&mut self, index: usize) -> T {
|
||||||
|
// Note: `<` because it's *not* valid to remove after everything
|
||||||
|
assert!(index < self.len, "index out of bounds");
|
||||||
|
unsafe {
|
||||||
|
self.len -= 1;
|
||||||
|
let result = ptr::read(self.ptr.offset(index as isize));
|
||||||
|
ptr::copy(self.ptr.offset(index as isize + 1),
|
||||||
|
self.ptr.offset(index as isize),
|
||||||
|
len - index);
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
147
src/doc/nomicon/vec-into-iter.md
Normal file
147
src/doc/nomicon/vec-into-iter.md
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
% IntoIter
|
||||||
|
|
||||||
|
Let's move on to writing iterators. `iter` and `iter_mut` have already been
|
||||||
|
written for us thanks to The Magic of Deref. However there's two interesting
|
||||||
|
iterators that Vec provides that slices can't: `into_iter` and `drain`.
|
||||||
|
|
||||||
|
IntoIter consumes the Vec by-value, and can consequently yield its elements
|
||||||
|
by-value. In order to enable this, IntoIter needs to take control of Vec's
|
||||||
|
allocation.
|
||||||
|
|
||||||
|
IntoIter needs to be DoubleEnded as well, to enable reading from both ends.
|
||||||
|
Reading from the back could just be implemented as calling `pop`, but reading
|
||||||
|
from the front is harder. We could call `remove(0)` but that would be insanely
|
||||||
|
expensive. Instead we're going to just use ptr::read to copy values out of
|
||||||
|
either end of the Vec without mutating the buffer at all.
|
||||||
|
|
||||||
|
To do this we're going to use a very common C idiom for array iteration. We'll
|
||||||
|
make two pointers; one that points to the start of the array, and one that
|
||||||
|
points to one-element past the end. When we want an element from one end, we'll
|
||||||
|
read out the value pointed to at that end and move the pointer over by one. When
|
||||||
|
the two pointers are equal, we know we're done.
|
||||||
|
|
||||||
|
Note that the order of read and offset are reversed for `next` and `next_back`
|
||||||
|
For `next_back` the pointer is always after the element it wants to read next,
|
||||||
|
while for `next` the pointer is always at the element it wants to read next.
|
||||||
|
To see why this is, consider the case where every element but one has been
|
||||||
|
yielded.
|
||||||
|
|
||||||
|
The array looks like this:
|
||||||
|
|
||||||
|
```text
|
||||||
|
S E
|
||||||
|
[X, X, X, O, X, X, X]
|
||||||
|
```
|
||||||
|
|
||||||
|
If E pointed directly at the element it wanted to yield next, it would be
|
||||||
|
indistinguishable from the case where there are no more elements to yield.
|
||||||
|
|
||||||
|
Although we don't actually care about it during iteration, we also need to hold
|
||||||
|
onto the Vec's allocation information in order to free it once IntoIter is
|
||||||
|
dropped.
|
||||||
|
|
||||||
|
So we're going to use the following struct:
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
struct IntoIter<T> {
|
||||||
|
buf: Unique<T>,
|
||||||
|
cap: usize,
|
||||||
|
start: *const T,
|
||||||
|
end: *const T,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
And this is what we end up with for initialization:
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
impl<T> Vec<T> {
|
||||||
|
fn into_iter(self) -> IntoIter<T> {
|
||||||
|
// Can't destructure Vec since it's Drop
|
||||||
|
let ptr = self.ptr;
|
||||||
|
let cap = self.cap;
|
||||||
|
let len = self.len;
|
||||||
|
|
||||||
|
// Make sure not to drop Vec since that will free the buffer
|
||||||
|
mem::forget(self);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
IntoIter {
|
||||||
|
buf: ptr,
|
||||||
|
cap: cap,
|
||||||
|
start: *ptr,
|
||||||
|
end: if cap == 0 {
|
||||||
|
// can't offset off this pointer, it's not allocated!
|
||||||
|
*ptr
|
||||||
|
} else {
|
||||||
|
ptr.offset(len as isize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Here's iterating forward:
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
impl<T> Iterator for IntoIter<T> {
|
||||||
|
type Item = T;
|
||||||
|
fn next(&mut self) -> Option<T> {
|
||||||
|
if self.start == self.end {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
unsafe {
|
||||||
|
let result = ptr::read(self.start);
|
||||||
|
self.start = self.start.offset(1);
|
||||||
|
Some(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||||
|
let len = (self.end as usize - self.start as usize)
|
||||||
|
/ mem::size_of::<T>();
|
||||||
|
(len, Some(len))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
And here's iterating backwards.
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
impl<T> DoubleEndedIterator for IntoIter<T> {
|
||||||
|
fn next_back(&mut self) -> Option<T> {
|
||||||
|
if self.start == self.end {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
unsafe {
|
||||||
|
self.end = self.end.offset(-1);
|
||||||
|
Some(ptr::read(self.end))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Because IntoIter takes ownership of its allocation, it needs to implement Drop
|
||||||
|
to free it. However it also wants to implement Drop to drop any elements it
|
||||||
|
contains that weren't yielded.
|
||||||
|
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
impl<T> Drop for IntoIter<T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if self.cap != 0 {
|
||||||
|
// drop any remaining elements
|
||||||
|
for _ in &mut *self {}
|
||||||
|
|
||||||
|
let align = mem::align_of::<T>();
|
||||||
|
let elem_size = mem::size_of::<T>();
|
||||||
|
let num_bytes = elem_size * self.cap;
|
||||||
|
unsafe {
|
||||||
|
heap::deallocate(*self.buf as *mut _, num_bytes, align);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
100
src/doc/nomicon/vec-layout.md
Normal file
100
src/doc/nomicon/vec-layout.md
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
% Layout
|
||||||
|
|
||||||
|
First off, we need to come up with the struct layout. A Vec has three parts:
|
||||||
|
a pointer to the allocation, the size of the allocation, and the number of
|
||||||
|
elements that have been initialized.
|
||||||
|
|
||||||
|
Naively, this means we just want this design:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
pub struct Vec<T> {
|
||||||
|
ptr: *mut T,
|
||||||
|
cap: usize,
|
||||||
|
len: usize,
|
||||||
|
}
|
||||||
|
# fn main() {}
|
||||||
|
```
|
||||||
|
|
||||||
|
And indeed this would compile. Unfortunately, it would be incorrect. First, the
|
||||||
|
compiler will give us too strict variance. So a `&Vec<&'static str>`
|
||||||
|
couldn't be used where an `&Vec<&'a str>` was expected. More importantly, it
|
||||||
|
will give incorrect ownership information to the drop checker, as it will
|
||||||
|
conservatively assume we don't own any values of type `T`. See [the chapter
|
||||||
|
on ownership and lifetimes][ownership] for all the details on variance and
|
||||||
|
drop check.
|
||||||
|
|
||||||
|
As we saw in the ownership chapter, we should use `Unique<T>` in place of
|
||||||
|
`*mut T` when we have a raw pointer to an allocation we own. Unique is unstable,
|
||||||
|
so we'd like to not use it if possible, though.
|
||||||
|
|
||||||
|
As a recap, Unique is a wrapper around a raw pointer that declares that:
|
||||||
|
|
||||||
|
* We are variant over `T`
|
||||||
|
* We may own a value of type `T` (for drop check)
|
||||||
|
* We are Send/Sync if `T` is Send/Sync
|
||||||
|
* We deref to `*mut T` (so it largely acts like a `*mut` in our code)
|
||||||
|
* Our pointer is never null (so `Option<Vec<T>>` is null-pointer-optimized)
|
||||||
|
|
||||||
|
We can implement all of the above requirements except for the last
|
||||||
|
one in stable Rust:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
use std::ops::Deref;
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
|
struct Unique<T> {
|
||||||
|
ptr: *const T, // *const for variance
|
||||||
|
_marker: PhantomData<T>, // For the drop checker
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deriving Send and Sync is safe because we are the Unique owners
|
||||||
|
// of this data. It's like Unique<T> is "just" T.
|
||||||
|
unsafe impl<T: Send> Send for Unique<T> {}
|
||||||
|
unsafe impl<T: Sync> Sync for Unique<T> {}
|
||||||
|
|
||||||
|
impl<T> Unique<T> {
|
||||||
|
pub fn new(ptr: *mut T) -> Self {
|
||||||
|
Unique { ptr: ptr, _marker: PhantomData }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Deref for Unique<T> {
|
||||||
|
type Target = *mut T;
|
||||||
|
fn deref(&self) -> &*mut T {
|
||||||
|
// There's no way to cast the *const to a *mut
|
||||||
|
// while also taking a reference. So we just
|
||||||
|
// transmute it since it's all "just pointers".
|
||||||
|
unsafe { mem::transmute(&self.ptr) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
# fn main() {}
|
||||||
|
```
|
||||||
|
|
||||||
|
Unfortunately the mechanism for stating that your value is non-zero is
|
||||||
|
unstable and unlikely to be stabilized soon. As such we're just going to
|
||||||
|
take the hit and use std's Unique:
|
||||||
|
|
||||||
|
|
||||||
|
```rust
|
||||||
|
#![feature(unique)]
|
||||||
|
|
||||||
|
use std::ptr::{Unique, self};
|
||||||
|
|
||||||
|
pub struct Vec<T> {
|
||||||
|
ptr: Unique<T>,
|
||||||
|
cap: usize,
|
||||||
|
len: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
# fn main() {}
|
||||||
|
```
|
||||||
|
|
||||||
|
If you don't care about the null-pointer optimization, then you can use the
|
||||||
|
stable code. However we will be designing the rest of the code around enabling
|
||||||
|
the optimization. In particular, `Unique::new` is unsafe to call, because
|
||||||
|
putting `null` inside of it is Undefined Behaviour. Our stable Unique doesn't
|
||||||
|
need `new` to be unsafe because it doesn't make any interesting guarantees about
|
||||||
|
its contents.
|
||||||
|
|
||||||
|
[ownership]: ownership.html
|
55
src/doc/nomicon/vec-push-pop.md
Normal file
55
src/doc/nomicon/vec-push-pop.md
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
% Push and Pop
|
||||||
|
|
||||||
|
Alright. We can initialize. We can allocate. Let's actually implement some
|
||||||
|
functionality! Let's start with `push`. All it needs to do is check if we're
|
||||||
|
full to grow, unconditionally write to the next index, and then increment our
|
||||||
|
length.
|
||||||
|
|
||||||
|
To do the write we have to be careful not to evaluate the memory we want to write
|
||||||
|
to. At worst, it's truly uninitialized memory from the allocator. At best it's the
|
||||||
|
bits of some old value we popped off. Either way, we can't just index to the memory
|
||||||
|
and dereference it, because that will evaluate the memory as a valid instance of
|
||||||
|
T. Worse, `foo[idx] = x` will try to call `drop` on the old value of `foo[idx]`!
|
||||||
|
|
||||||
|
The correct way to do this is with `ptr::write`, which just blindly overwrites the
|
||||||
|
target address with the bits of the value we provide. No evaluation involved.
|
||||||
|
|
||||||
|
For `push`, if the old len (before push was called) is 0, then we want to write
|
||||||
|
to the 0th index. So we should offset by the old len.
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
pub fn push(&mut self, elem: T) {
|
||||||
|
if self.len == self.cap { self.grow(); }
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
ptr::write(self.ptr.offset(self.len as isize), elem);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Can't fail, we'll OOM first.
|
||||||
|
self.len += 1;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Easy! How about `pop`? Although this time the index we want to access is
|
||||||
|
initialized, Rust won't just let us dereference the location of memory to move
|
||||||
|
the value out, because that would leave the memory uninitialized! For this we
|
||||||
|
need `ptr::read`, which just copies out the bits from the target address and
|
||||||
|
intrprets it as a value of type T. This will leave the memory at this address
|
||||||
|
logically uninitialized, even though there is in fact a perfectly good instance
|
||||||
|
of T there.
|
||||||
|
|
||||||
|
For `pop`, if the old len is 1, we want to read out of the 0th index. So we
|
||||||
|
should offset by the new len.
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
pub fn pop(&mut self) -> Option<T> {
|
||||||
|
if self.len == 0 {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
self.len -= 1;
|
||||||
|
unsafe {
|
||||||
|
Some(ptr::read(self.ptr.offset(self.len as isize)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
136
src/doc/nomicon/vec-raw.md
Normal file
136
src/doc/nomicon/vec-raw.md
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
% RawVec
|
||||||
|
|
||||||
|
We've actually reached an interesting situation here: we've duplicated the logic
|
||||||
|
for specifying a buffer and freeing its memory in Vec and IntoIter. Now that
|
||||||
|
we've implemented it and identified *actual* logic duplication, this is a good
|
||||||
|
time to perform some logic compression.
|
||||||
|
|
||||||
|
We're going to abstract out the `(ptr, cap)` pair and give them the logic for
|
||||||
|
allocating, growing, and freeing:
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
struct RawVec<T> {
|
||||||
|
ptr: Unique<T>,
|
||||||
|
cap: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> RawVec<T> {
|
||||||
|
fn new() -> Self {
|
||||||
|
assert!(mem::size_of::<T>() != 0, "TODO: implement ZST support");
|
||||||
|
unsafe {
|
||||||
|
RawVec { ptr: Unique::new(heap::EMPTY as *mut T), cap: 0 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// unchanged from Vec
|
||||||
|
fn grow(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
let align = mem::align_of::<T>();
|
||||||
|
let elem_size = mem::size_of::<T>();
|
||||||
|
|
||||||
|
let (new_cap, ptr) = if self.cap == 0 {
|
||||||
|
let ptr = heap::allocate(elem_size, align);
|
||||||
|
(1, ptr)
|
||||||
|
} else {
|
||||||
|
let new_cap = 2 * self.cap;
|
||||||
|
let ptr = heap::reallocate(*self.ptr as *mut _,
|
||||||
|
self.cap * elem_size,
|
||||||
|
new_cap * elem_size,
|
||||||
|
align);
|
||||||
|
(new_cap, ptr)
|
||||||
|
};
|
||||||
|
|
||||||
|
// If allocate or reallocate fail, we'll get `null` back
|
||||||
|
if ptr.is_null() { oom() }
|
||||||
|
|
||||||
|
self.ptr = Unique::new(ptr as *mut _);
|
||||||
|
self.cap = new_cap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl<T> Drop for RawVec<T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if self.cap != 0 {
|
||||||
|
let align = mem::align_of::<T>();
|
||||||
|
let elem_size = mem::size_of::<T>();
|
||||||
|
let num_bytes = elem_size * self.cap;
|
||||||
|
unsafe {
|
||||||
|
heap::deallocate(*self.ptr as *mut _, num_bytes, align);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
And change Vec as follows:
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
pub struct Vec<T> {
|
||||||
|
buf: RawVec<T>,
|
||||||
|
len: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Vec<T> {
|
||||||
|
fn ptr(&self) -> *mut T { *self.buf.ptr }
|
||||||
|
|
||||||
|
fn cap(&self) -> usize { self.buf.cap }
|
||||||
|
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Vec { buf: RawVec::new(), len: 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
// push/pop/insert/remove largely unchanged:
|
||||||
|
// * `self.ptr -> self.ptr()`
|
||||||
|
// * `self.cap -> self.cap()`
|
||||||
|
// * `self.grow -> self.buf.grow()`
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Drop for Vec<T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
while let Some(_) = self.pop() {}
|
||||||
|
// deallocation is handled by RawVec
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
And finally we can really simplify IntoIter:
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
struct IntoIter<T> {
|
||||||
|
_buf: RawVec<T>, // we don't actually care about this. Just need it to live.
|
||||||
|
start: *const T,
|
||||||
|
end: *const T,
|
||||||
|
}
|
||||||
|
|
||||||
|
// next and next_back literally unchanged since they never referred to the buf
|
||||||
|
|
||||||
|
impl<T> Drop for IntoIter<T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
// only need to ensure all our elements are read;
|
||||||
|
// buffer will clean itself up afterwards.
|
||||||
|
for _ in &mut *self {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Vec<T> {
|
||||||
|
pub fn into_iter(self) -> IntoIter<T> {
|
||||||
|
unsafe {
|
||||||
|
// need to use ptr::read to unsafely move the buf out since it's
|
||||||
|
// not Copy, and Vec implements Drop (so we can't destructure it).
|
||||||
|
let buf = ptr::read(&self.buf);
|
||||||
|
let len = self.len;
|
||||||
|
mem::forget(self);
|
||||||
|
|
||||||
|
IntoIter {
|
||||||
|
start: *buf.ptr,
|
||||||
|
end: buf.ptr.offset(len as isize),
|
||||||
|
_buf: buf,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Much better.
|
176
src/doc/nomicon/vec-zsts.md
Normal file
176
src/doc/nomicon/vec-zsts.md
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
% Handling Zero-Sized Types
|
||||||
|
|
||||||
|
It's time. We're going to fight the spectre that is zero-sized types. Safe Rust
|
||||||
|
*never* needs to care about this, but Vec is very intensive on raw pointers and
|
||||||
|
raw allocations, which are exactly the two things that care about
|
||||||
|
zero-sized types. We need to be careful of two things:
|
||||||
|
|
||||||
|
* The raw allocator API has undefined behaviour if you pass in 0 for an
|
||||||
|
allocation size.
|
||||||
|
* raw pointer offsets are no-ops for zero-sized types, which will break our
|
||||||
|
C-style pointer iterator.
|
||||||
|
|
||||||
|
Thankfully we abstracted out pointer-iterators and allocating handling into
|
||||||
|
RawValIter and RawVec respectively. How mysteriously convenient.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Allocating Zero-Sized Types
|
||||||
|
|
||||||
|
So if the allocator API doesn't support zero-sized allocations, what on earth
|
||||||
|
do we store as our allocation? Why, `heap::EMPTY` of course! Almost every operation
|
||||||
|
with a ZST is a no-op since ZSTs have exactly one value, and therefore no state needs
|
||||||
|
to be considered to store or load them. This actually extends to `ptr::read` and
|
||||||
|
`ptr::write`: they won't actually look at the pointer at all. As such we never need
|
||||||
|
to change the pointer.
|
||||||
|
|
||||||
|
Note however that our previous reliance on running out of memory before overflow is
|
||||||
|
no longer valid with zero-sized types. We must explicitly guard against capacity
|
||||||
|
overflow for zero-sized types.
|
||||||
|
|
||||||
|
Due to our current architecture, all this means is writing 3 guards, one in each
|
||||||
|
method of RawVec.
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
impl<T> RawVec<T> {
|
||||||
|
fn new() -> Self {
|
||||||
|
unsafe {
|
||||||
|
// !0 is usize::MAX. This branch should be stripped at compile time.
|
||||||
|
let cap = if mem::size_of::<T>() == 0 { !0 } else { 0 };
|
||||||
|
|
||||||
|
// heap::EMPTY doubles as "unallocated" and "zero-sized allocation"
|
||||||
|
RawVec { ptr: Unique::new(heap::EMPTY as *mut T), cap: cap }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn grow(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
let elem_size = mem::size_of::<T>();
|
||||||
|
|
||||||
|
// since we set the capacity to usize::MAX when elem_size is
|
||||||
|
// 0, getting to here necessarily means the Vec is overfull.
|
||||||
|
assert!(elem_size != 0, "capacity overflow");
|
||||||
|
|
||||||
|
let align = mem::align_of::<T>();
|
||||||
|
|
||||||
|
let (new_cap, ptr) = if self.cap == 0 {
|
||||||
|
let ptr = heap::allocate(elem_size, align);
|
||||||
|
(1, ptr)
|
||||||
|
} else {
|
||||||
|
let new_cap = 2 * self.cap;
|
||||||
|
let ptr = heap::reallocate(*self.ptr as *mut _,
|
||||||
|
self.cap * elem_size,
|
||||||
|
new_cap * elem_size,
|
||||||
|
align);
|
||||||
|
(new_cap, ptr)
|
||||||
|
};
|
||||||
|
|
||||||
|
// If allocate or reallocate fail, we'll get `null` back
|
||||||
|
if ptr.is_null() { oom() }
|
||||||
|
|
||||||
|
self.ptr = Unique::new(ptr as *mut _);
|
||||||
|
self.cap = new_cap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Drop for RawVec<T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
let elem_size = mem::size_of::<T>();
|
||||||
|
|
||||||
|
// don't free zero-sized allocations, as they were never allocated.
|
||||||
|
if self.cap != 0 && elem_size != 0 {
|
||||||
|
let align = mem::align_of::<T>();
|
||||||
|
|
||||||
|
let num_bytes = elem_size * self.cap;
|
||||||
|
unsafe {
|
||||||
|
heap::deallocate(*self.ptr as *mut _, num_bytes, align);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
That's it. We support pushing and popping zero-sized types now. Our iterators
|
||||||
|
(that aren't provided by slice Deref) are still busted, though.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Iterating Zero-Sized Types
|
||||||
|
|
||||||
|
Zero-sized offsets are no-ops. This means that our current design will always
|
||||||
|
initialize `start` and `end` as the same value, and our iterators will yield
|
||||||
|
nothing. The current solution to this is to cast the pointers to integers,
|
||||||
|
increment, and then cast them back:
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
impl<T> RawValIter<T> {
|
||||||
|
unsafe fn new(slice: &[T]) -> Self {
|
||||||
|
RawValIter {
|
||||||
|
start: slice.as_ptr(),
|
||||||
|
end: if mem::size_of::<T>() == 0 {
|
||||||
|
((slice.as_ptr() as usize) + slice.len()) as *const _
|
||||||
|
} else if slice.len() == 0 {
|
||||||
|
slice.as_ptr()
|
||||||
|
} else {
|
||||||
|
slice.as_ptr().offset(slice.len() as isize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Now we have a different bug. Instead of our iterators not running at all, our
|
||||||
|
iterators now run *forever*. We need to do the same trick in our iterator impls.
|
||||||
|
Also, our size_hint computation code will divide by 0 for ZSTs. Since we'll
|
||||||
|
basically be treating the two pointers as if they point to bytes, we'll just
|
||||||
|
map size 0 to divide by 1.
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
impl<T> Iterator for RawValIter<T> {
|
||||||
|
type Item = T;
|
||||||
|
fn next(&mut self) -> Option<T> {
|
||||||
|
if self.start == self.end {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
unsafe {
|
||||||
|
let result = ptr::read(self.start);
|
||||||
|
self.start = if mem::size_of::<T>() == 0 {
|
||||||
|
(self.start as usize + 1) as *const _
|
||||||
|
} else {
|
||||||
|
self.start.offset(1);
|
||||||
|
}
|
||||||
|
Some(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||||
|
let elem_size = mem::size_of::<T>();
|
||||||
|
let len = (self.end as usize - self.start as usize)
|
||||||
|
/ if elem_size == 0 { 1 } else { elem_size };
|
||||||
|
(len, Some(len))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> DoubleEndedIterator for RawValIter<T> {
|
||||||
|
fn next_back(&mut self) -> Option<T> {
|
||||||
|
if self.start == self.end {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
unsafe {
|
||||||
|
self.end = if mem::size_of::<T>() == 0 {
|
||||||
|
(self.end as usize - 1) as *const _
|
||||||
|
} else {
|
||||||
|
self.end.offset(-1);
|
||||||
|
}
|
||||||
|
Some(ptr::read(self.end))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
And that's it. Iteration works!
|
20
src/doc/nomicon/vec.md
Normal file
20
src/doc/nomicon/vec.md
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
% Example: Implementing Vec
|
||||||
|
|
||||||
|
To bring everything together, we're going to write `std::Vec` from scratch.
|
||||||
|
Because all the best tools for writing unsafe code are unstable, this
|
||||||
|
project will only work on nightly (as of Rust 1.2.0). With the exception of the
|
||||||
|
allocator API, much of the unstable code we'll use is expected to be stabilized
|
||||||
|
in a similar form as it is today.
|
||||||
|
|
||||||
|
However we will generally try to avoid unstable code where possible. In
|
||||||
|
particular we won't use any intrinsics that could make a code a little
|
||||||
|
bit nicer or efficient because intrinsics are permanently unstable. Although
|
||||||
|
many intrinsics *do* become stabilized elsewhere (`std::ptr` and `str::mem`
|
||||||
|
consist of many intrinsics).
|
||||||
|
|
||||||
|
Ultimately this means our implementation may not take advantage of all
|
||||||
|
possible optimizations, though it will be by no means *naive*. We will
|
||||||
|
definitely get into the weeds over nitty-gritty details, even
|
||||||
|
when the problem doesn't *really* merit it.
|
||||||
|
|
||||||
|
You wanted advanced. We're gonna go advanced.
|
119
src/doc/nomicon/working-with-unsafe.md
Normal file
119
src/doc/nomicon/working-with-unsafe.md
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
% Working with Unsafe
|
||||||
|
|
||||||
|
Rust generally only gives us the tools to talk about Unsafe Rust in a scoped and
|
||||||
|
binary manner. Unfortunately, reality is significantly more complicated than
|
||||||
|
that. For instance, consider the following toy function:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn index(idx: usize, arr: &[u8]) -> Option<u8> {
|
||||||
|
if idx < arr.len() {
|
||||||
|
unsafe {
|
||||||
|
Some(*arr.get_unchecked(idx))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Clearly, this function is safe. We check that the index is in bounds, and if it
|
||||||
|
is, index into the array in an unchecked manner. But even in such a trivial
|
||||||
|
function, the scope of the unsafe block is questionable. Consider changing the
|
||||||
|
`<` to a `<=`:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn index(idx: usize, arr: &[u8]) -> Option<u8> {
|
||||||
|
if idx <= arr.len() {
|
||||||
|
unsafe {
|
||||||
|
Some(*arr.get_unchecked(idx))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This program is now unsound, and yet *we only modified safe code*. This is the
|
||||||
|
fundamental problem of safety: it's non-local. The soundness of our unsafe
|
||||||
|
operations necessarily depends on the state established by otherwise
|
||||||
|
"safe" operations.
|
||||||
|
|
||||||
|
Safety is modular in the sense that opting into unsafety doesn't require you
|
||||||
|
to consider arbitrary other kinds of badness. For instance, doing an unchecked
|
||||||
|
index into a slice doesn't mean you suddenly need to worry about the slice being
|
||||||
|
null or containing uninitialized memory. Nothing fundamentally changes. However
|
||||||
|
safety *isn't* modular in the sense that programs are inherently stateful and
|
||||||
|
your unsafe operations may depend on arbitrary other state.
|
||||||
|
|
||||||
|
Trickier than that is when we get into actual statefulness. Consider a simple
|
||||||
|
implementation of `Vec`:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use std::ptr;
|
||||||
|
|
||||||
|
// Note this definition is insufficient. See the section on implementing Vec.
|
||||||
|
pub struct Vec<T> {
|
||||||
|
ptr: *mut T,
|
||||||
|
len: usize,
|
||||||
|
cap: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note this implementation does not correctly handle zero-sized types.
|
||||||
|
// We currently live in a nice imaginary world of only positive fixed-size
|
||||||
|
// types.
|
||||||
|
impl<T> Vec<T> {
|
||||||
|
pub fn push(&mut self, elem: T) {
|
||||||
|
if self.len == self.cap {
|
||||||
|
// not important for this example
|
||||||
|
self.reallocate();
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
ptr::write(self.ptr.offset(self.len as isize), elem);
|
||||||
|
self.len += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# fn reallocate(&mut self) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
# fn main() {}
|
||||||
|
```
|
||||||
|
|
||||||
|
This code is simple enough to reasonably audit and verify. Now consider
|
||||||
|
adding the following method:
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
fn make_room(&mut self) {
|
||||||
|
// grow the capacity
|
||||||
|
self.cap += 1;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This code is 100% Safe Rust but it is also completely unsound. Changing the
|
||||||
|
capacity violates the invariants of Vec (that `cap` reflects the allocated space
|
||||||
|
in the Vec). This is not something the rest of Vec can guard against. It *has*
|
||||||
|
to trust the capacity field because there's no way to verify it.
|
||||||
|
|
||||||
|
`unsafe` does more than pollute a whole function: it pollutes a whole *module*.
|
||||||
|
Generally, the only bullet-proof way to limit the scope of unsafe code is at the
|
||||||
|
module boundary with privacy.
|
||||||
|
|
||||||
|
However this works *perfectly*. The existence of `make_room` is *not* a
|
||||||
|
problem for the soundness of Vec because we didn't mark it as public. Only the
|
||||||
|
module that defines this function can call it. Also, `make_room` directly
|
||||||
|
accesses the private fields of Vec, so it can only be written in the same module
|
||||||
|
as Vec.
|
||||||
|
|
||||||
|
It is therefore possible for us to write a completely safe abstraction that
|
||||||
|
relies on complex invariants. This is *critical* to the relationship between
|
||||||
|
Safe Rust and Unsafe Rust. We have already seen that Unsafe code must trust
|
||||||
|
*some* Safe code, but can't trust *generic* Safe code. It can't trust an
|
||||||
|
arbitrary implementor of a trait or any function that was passed to it to be
|
||||||
|
well-behaved in a way that safe code doesn't care about.
|
||||||
|
|
||||||
|
However if unsafe code couldn't prevent client safe code from messing with its
|
||||||
|
state in arbitrary ways, safety would be a lost cause. Thankfully, it *can*
|
||||||
|
prevent arbitrary code from messing with critical state due to privacy.
|
||||||
|
|
||||||
|
Safety lives!
|
||||||
|
|
@ -338,12 +338,16 @@ type of the literal. The integer suffix must be the name of one of the
|
|||||||
integral types: `u8`, `i8`, `u16`, `i16`, `u32`, `i32`, `u64`, `i64`,
|
integral types: `u8`, `i8`, `u16`, `i16`, `u32`, `i32`, `u64`, `i64`,
|
||||||
`isize`, or `usize`.
|
`isize`, or `usize`.
|
||||||
|
|
||||||
The type of an _unsuffixed_ integer literal is determined by type inference.
|
The type of an _unsuffixed_ integer literal is determined by type inference:
|
||||||
If an integer type can be _uniquely_ determined from the surrounding program
|
|
||||||
context, the unsuffixed integer literal has that type. If the program context
|
* If an integer type can be _uniquely_ determined from the surrounding
|
||||||
underconstrains the type, it defaults to the signed 32-bit integer `i32`; if
|
program context, the unsuffixed integer literal has that type.
|
||||||
the program context overconstrains the type, it is considered a static type
|
|
||||||
error.
|
* If the program context under-constrains the type, it defaults to the
|
||||||
|
signed 32-bit integer `i32`.
|
||||||
|
|
||||||
|
* If the program context over-constrains the type, it is considered a
|
||||||
|
static type error.
|
||||||
|
|
||||||
Examples of integer literals of various forms:
|
Examples of integer literals of various forms:
|
||||||
|
|
||||||
@ -371,12 +375,17 @@ The suffix forcibly sets the type of the literal. There are two valid
|
|||||||
_floating-point suffixes_, `f32` and `f64` (the 32-bit and 64-bit floating point
|
_floating-point suffixes_, `f32` and `f64` (the 32-bit and 64-bit floating point
|
||||||
types), which explicitly determine the type of the literal.
|
types), which explicitly determine the type of the literal.
|
||||||
|
|
||||||
The type of an _unsuffixed_ floating-point literal is determined by type
|
The type of an _unsuffixed_ floating-point literal is determined by
|
||||||
inference. If a floating-point type can be _uniquely_ determined from the
|
type inference:
|
||||||
surrounding program context, the unsuffixed floating-point literal has that type.
|
|
||||||
If the program context underconstrains the type, it defaults to double-precision `f64`;
|
* If a floating-point type can be _uniquely_ determined from the
|
||||||
if the program context overconstrains the type, it is considered a static type
|
surrounding program context, the unsuffixed floating-point literal
|
||||||
error.
|
has that type.
|
||||||
|
|
||||||
|
* If the program context under-constrains the type, it defaults to `f64`.
|
||||||
|
|
||||||
|
* If the program context over-constrains the type, it is considered a
|
||||||
|
static type error.
|
||||||
|
|
||||||
Examples of floating-point literals of various forms:
|
Examples of floating-point literals of various forms:
|
||||||
|
|
||||||
@ -582,8 +591,9 @@ always been designed to be compiled. For these reasons, this section assumes a
|
|||||||
compiler.
|
compiler.
|
||||||
|
|
||||||
Rust's semantics obey a *phase distinction* between compile-time and
|
Rust's semantics obey a *phase distinction* between compile-time and
|
||||||
run-time.[^phase-distinction] Those semantic rules that have a *static
|
run-time.[^phase-distinction] Semantic rules that have a *static
|
||||||
interpretation* govern the success or failure of compilation. Those semantics
|
interpretation* govern the success or failure of compilation, while
|
||||||
|
semantic rules
|
||||||
that have a *dynamic interpretation* govern the behavior of the program at
|
that have a *dynamic interpretation* govern the behavior of the program at
|
||||||
run-time.
|
run-time.
|
||||||
|
|
||||||
@ -1047,11 +1057,8 @@ This is a list of behavior not considered *unsafe* in Rust terms, but that may
|
|||||||
be undesired.
|
be undesired.
|
||||||
|
|
||||||
* Deadlocks
|
* Deadlocks
|
||||||
* Reading data from private fields (`std::repr`)
|
|
||||||
* Leaks of memory and other resources
|
* Leaks of memory and other resources
|
||||||
* Exiting without calling destructors
|
* Exiting without calling destructors
|
||||||
* Sending signals
|
|
||||||
* Accessing/modifying the file system
|
|
||||||
* Integer overflow
|
* Integer overflow
|
||||||
- Overflow is considered "unexpected" behavior and is always user-error,
|
- Overflow is considered "unexpected" behavior and is always user-error,
|
||||||
unless the `wrapping` primitives are used. In non-optimized builds, the compiler
|
unless the `wrapping` primitives are used. In non-optimized builds, the compiler
|
||||||
@ -1192,8 +1199,8 @@ An example of an `enum` item and its use:
|
|||||||
|
|
||||||
```
|
```
|
||||||
enum Animal {
|
enum Animal {
|
||||||
Dog,
|
Dog,
|
||||||
Cat
|
Cat,
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut a: Animal = Animal::Dog;
|
let mut a: Animal = Animal::Dog;
|
||||||
@ -1286,7 +1293,7 @@ All access to a static is safe, but there are a number of restrictions on
|
|||||||
statics:
|
statics:
|
||||||
|
|
||||||
* Statics may not contain any destructors.
|
* Statics may not contain any destructors.
|
||||||
* The types of static values must ascribe to `Sync` to allow threadsafe access.
|
* The types of static values must ascribe to `Sync` to allow thread-safe access.
|
||||||
* Statics may not refer to other statics by value, only by reference.
|
* Statics may not refer to other statics by value, only by reference.
|
||||||
* Constants cannot refer to statics.
|
* Constants cannot refer to statics.
|
||||||
|
|
||||||
@ -1630,6 +1637,10 @@ The type of a function declared in an extern block is `extern "abi" fn(A1, ...,
|
|||||||
An) -> R`, where `A1...An` are the declared types of its arguments and `R` is
|
An) -> R`, where `A1...An` are the declared types of its arguments and `R` is
|
||||||
the declared return type.
|
the declared return type.
|
||||||
|
|
||||||
|
It is valid to add the `link` attribute on an empty extern block. You can use
|
||||||
|
this to satisfy the linking requirements of extern blocks elsewhere in your code
|
||||||
|
(including upstream crates) instead of adding the attribute to each extern block.
|
||||||
|
|
||||||
## Visibility and Privacy
|
## Visibility and Privacy
|
||||||
|
|
||||||
These two terms are often used interchangeably, and what they are attempting to
|
These two terms are often used interchangeably, and what they are attempting to
|
||||||
@ -1688,7 +1699,7 @@ explain, here's a few use cases and what they would entail:
|
|||||||
* A crate needs a global available "helper module" to itself, but it doesn't
|
* A crate needs a global available "helper module" to itself, but it doesn't
|
||||||
want to expose the helper module as a public API. To accomplish this, the
|
want to expose the helper module as a public API. To accomplish this, the
|
||||||
root of the crate's hierarchy would have a private module which then
|
root of the crate's hierarchy would have a private module which then
|
||||||
internally has a "public api". Because the entire crate is a descendant of
|
internally has a "public API". Because the entire crate is a descendant of
|
||||||
the root, then the entire local crate can access this private module through
|
the root, then the entire local crate can access this private module through
|
||||||
the second case.
|
the second case.
|
||||||
|
|
||||||
@ -1951,8 +1962,6 @@ macro scope.
|
|||||||
object file that this item's contents will be placed into.
|
object file that this item's contents will be placed into.
|
||||||
- `no_mangle` - on any item, do not apply the standard name mangling. Set the
|
- `no_mangle` - on any item, do not apply the standard name mangling. Set the
|
||||||
symbol for this item to its identifier.
|
symbol for this item to its identifier.
|
||||||
- `packed` - on structs or enums, eliminate any padding that would be used to
|
|
||||||
align fields.
|
|
||||||
- `simd` - on certain tuple structs, derive the arithmetic operators, which
|
- `simd` - on certain tuple structs, derive the arithmetic operators, which
|
||||||
lower to the target's SIMD instructions, if any; the `simd` feature gate
|
lower to the target's SIMD instructions, if any; the `simd` feature gate
|
||||||
is necessary to use this attribute.
|
is necessary to use this attribute.
|
||||||
@ -2026,7 +2035,7 @@ The following configurations must be defined by the implementation:
|
|||||||
as a configuration itself, like `unix` or `windows`.
|
as a configuration itself, like `unix` or `windows`.
|
||||||
* `target_os = "..."`. Operating system of the target, examples include
|
* `target_os = "..."`. Operating system of the target, examples include
|
||||||
`"windows"`, `"macos"`, `"ios"`, `"linux"`, `"android"`, `"freebsd"`, `"dragonfly"`,
|
`"windows"`, `"macos"`, `"ios"`, `"linux"`, `"android"`, `"freebsd"`, `"dragonfly"`,
|
||||||
`"bitrig"` or `"openbsd"`.
|
`"bitrig"` , `"openbsd"` or `"netbsd"`.
|
||||||
* `target_pointer_width = "..."`. Target pointer width in bits. This is set
|
* `target_pointer_width = "..."`. Target pointer width in bits. This is set
|
||||||
to `"32"` for targets with 32-bit pointers, and likewise set to `"64"` for
|
to `"32"` for targets with 32-bit pointers, and likewise set to `"64"` for
|
||||||
64-bit pointers.
|
64-bit pointers.
|
||||||
@ -2360,6 +2369,8 @@ The currently implemented features of the reference compiler are:
|
|||||||
internally without imposing on callers
|
internally without imposing on callers
|
||||||
(i.e. making them behave like function calls in
|
(i.e. making them behave like function calls in
|
||||||
terms of encapsulation).
|
terms of encapsulation).
|
||||||
|
* - `default_type_parameter_fallback` - Allows type parameter defaults to
|
||||||
|
influence type inference.
|
||||||
|
|
||||||
If a feature is promoted to a language feature, then all existing programs will
|
If a feature is promoted to a language feature, then all existing programs will
|
||||||
start to receive compilation warnings about `#![feature]` directives which enabled
|
start to receive compilation warnings about `#![feature]` directives which enabled
|
||||||
@ -2509,9 +2520,8 @@ Here are some examples:
|
|||||||
#### Moved and copied types
|
#### Moved and copied types
|
||||||
|
|
||||||
When a [local variable](#variables) is used as an
|
When a [local variable](#variables) is used as an
|
||||||
[rvalue](#lvalues,-rvalues-and-temporaries) the variable will either be moved
|
[rvalue](#lvalues,-rvalues-and-temporaries), the variable will be copied
|
||||||
or copied, depending on its type. All values whose type implements `Copy` are
|
if its type implements `Copy`. All others are moved.
|
||||||
copied, all others are moved.
|
|
||||||
|
|
||||||
### Literal expressions
|
### Literal expressions
|
||||||
|
|
||||||
@ -2876,7 +2886,6 @@ operand.
|
|||||||
```
|
```
|
||||||
# let mut x = 0;
|
# let mut x = 0;
|
||||||
# let y = 0;
|
# let y = 0;
|
||||||
|
|
||||||
x = y;
|
x = y;
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -2966,14 +2975,12 @@ move values (depending on their type) from the environment into the lambda
|
|||||||
expression's captured environment.
|
expression's captured environment.
|
||||||
|
|
||||||
In this example, we define a function `ten_times` that takes a higher-order
|
In this example, we define a function `ten_times` that takes a higher-order
|
||||||
function argument, and call it with a lambda expression as an argument:
|
function argument, and we then call it with a lambda expression as an argument:
|
||||||
|
|
||||||
```
|
```
|
||||||
fn ten_times<F>(f: F) where F: Fn(i32) {
|
fn ten_times<F>(f: F) where F: Fn(i32) {
|
||||||
let mut i = 0i32;
|
for index in 0..10 {
|
||||||
while i < 10 {
|
f(index);
|
||||||
f(i);
|
|
||||||
i += 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3322,10 +3329,13 @@ An example of a tuple type and its use:
|
|||||||
|
|
||||||
```
|
```
|
||||||
type Pair<'a> = (i32, &'a str);
|
type Pair<'a> = (i32, &'a str);
|
||||||
let p: Pair<'static> = (10, "hello");
|
let p: Pair<'static> = (10, "ten");
|
||||||
let (a, b) = p;
|
let (a, b) = p;
|
||||||
assert!(b != "world");
|
|
||||||
assert!(p.0 == 10);
|
assert_eq!(a, 10);
|
||||||
|
assert_eq!(b, "ten");
|
||||||
|
assert_eq!(p.0, 10);
|
||||||
|
assert_eq!(p.1, "ten");
|
||||||
```
|
```
|
||||||
|
|
||||||
For historical reasons and convenience, the tuple type with no elements (`()`)
|
For historical reasons and convenience, the tuple type with no elements (`()`)
|
||||||
@ -3335,8 +3345,8 @@ is often called ‘unit’ or ‘the unit type’.
|
|||||||
|
|
||||||
Rust has two different types for a list of items:
|
Rust has two different types for a list of items:
|
||||||
|
|
||||||
* `[T; N]`, an 'array'.
|
* `[T; N]`, an 'array'
|
||||||
* `&[T]`, a 'slice'.
|
* `&[T]`, a 'slice'
|
||||||
|
|
||||||
An array has a fixed size, and can be allocated on either the stack or the
|
An array has a fixed size, and can be allocated on either the stack or the
|
||||||
heap.
|
heap.
|
||||||
@ -3344,18 +3354,23 @@ heap.
|
|||||||
A slice is a 'view' into an array. It doesn't own the data it points
|
A slice is a 'view' into an array. It doesn't own the data it points
|
||||||
to, it borrows it.
|
to, it borrows it.
|
||||||
|
|
||||||
An example of each kind:
|
Examples:
|
||||||
|
|
||||||
```{rust}
|
```{rust}
|
||||||
let vec: Vec<i32> = vec![1, 2, 3];
|
// A stack-allocated array
|
||||||
let arr: [i32; 3] = [1, 2, 3];
|
let array: [i32; 3] = [1, 2, 3];
|
||||||
let s: &[i32] = &vec[..];
|
|
||||||
|
// A heap-allocated array
|
||||||
|
let vector: Vec<i32> = vec![1, 2, 3];
|
||||||
|
|
||||||
|
// A slice into an array
|
||||||
|
let slice: &[i32] = &vector[..];
|
||||||
```
|
```
|
||||||
|
|
||||||
As you can see, the `vec!` macro allows you to create a `Vec<T>` easily. The
|
As you can see, the `vec!` macro allows you to create a `Vec<T>` easily. The
|
||||||
`vec!` macro is also part of the standard library, rather than the language.
|
`vec!` macro is also part of the standard library, rather than the language.
|
||||||
|
|
||||||
All in-bounds elements of arrays, and slices are always initialized, and access
|
All in-bounds elements of arrays and slices are always initialized, and access
|
||||||
to an array or slice is always bounds-checked.
|
to an array or slice is always bounds-checked.
|
||||||
|
|
||||||
### Structure types
|
### Structure types
|
||||||
@ -3489,7 +3504,7 @@ x = bo(5,7);
|
|||||||
|
|
||||||
#### Function types for specific items
|
#### Function types for specific items
|
||||||
|
|
||||||
Internally to the compiler, there are also function types that are specific to a particular
|
Internal to the compiler, there are also function types that are specific to a particular
|
||||||
function item. In the following snippet, for example, the internal types of the functions
|
function item. In the following snippet, for example, the internal types of the functions
|
||||||
`foo` and `bar` are different, despite the fact that they have the same signature:
|
`foo` and `bar` are different, despite the fact that they have the same signature:
|
||||||
|
|
||||||
@ -3517,13 +3532,14 @@ more of the closure traits:
|
|||||||
|
|
||||||
* `FnMut`
|
* `FnMut`
|
||||||
: The closure can be called multiple times as mutable. A closure called as
|
: The closure can be called multiple times as mutable. A closure called as
|
||||||
`FnMut` can mutate values from its environment. `FnMut` implies
|
`FnMut` can mutate values from its environment. `FnMut` inherits from
|
||||||
`FnOnce`.
|
`FnOnce` (i.e. anything implementing `FnMut` also implements `FnOnce`).
|
||||||
|
|
||||||
* `Fn`
|
* `Fn`
|
||||||
: The closure can be called multiple times through a shared reference.
|
: The closure can be called multiple times through a shared reference.
|
||||||
A closure called as `Fn` can neither move out from nor mutate values
|
A closure called as `Fn` can neither move out from nor mutate values
|
||||||
from its environment. `Fn` implies `FnMut` and `FnOnce`.
|
from its environment. `Fn` inherits from `FnMut`, which itself
|
||||||
|
inherits from `FnOnce`.
|
||||||
|
|
||||||
|
|
||||||
### Trait objects
|
### Trait objects
|
||||||
@ -3646,53 +3662,77 @@ Coercions are defined in [RFC401]. A coercion is implicit and has no syntax.
|
|||||||
### Coercion sites
|
### Coercion sites
|
||||||
|
|
||||||
A coercion can only occur at certain coercion sites in a program; these are
|
A coercion can only occur at certain coercion sites in a program; these are
|
||||||
typically places where the desired type is explicit or can be dervied by
|
typically places where the desired type is explicit or can be derived by
|
||||||
propagation from explicit types (without type inference). Possible coercion
|
propagation from explicit types (without type inference). Possible coercion
|
||||||
sites are:
|
sites are:
|
||||||
|
|
||||||
* `let` statements where an explicit type is given.
|
* `let` statements where an explicit type is given.
|
||||||
|
|
||||||
In `let _: U = e;`, `e` is coerced to have type `U`.
|
For example, `128` is coerced to have type `i8` in the following:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let _: i8 = 128;
|
||||||
|
```
|
||||||
|
|
||||||
* `static` and `const` statements (similar to `let` statements).
|
* `static` and `const` statements (similar to `let` statements).
|
||||||
|
|
||||||
* arguments for function calls.
|
* Arguments for function calls
|
||||||
|
|
||||||
The value being coerced is the
|
The value being coerced is the actual parameter, and it is coerced to
|
||||||
actual parameter and it is coerced to the type of the formal parameter. For
|
the type of the formal parameter.
|
||||||
example, let `foo` be defined as `fn foo(x: U) { ... }` and call it as
|
|
||||||
`foo(e);`. Then `e` is coerced to have type `U`;
|
|
||||||
|
|
||||||
* instantiations of struct or variant fields.
|
For example, `128` is coerced to have type `i8` in the following:
|
||||||
|
|
||||||
Assume we have a `struct
|
```rust
|
||||||
Foo { x: U }` and instantiate it as `Foo { x: e }`. Then `e` is coerced to
|
fn bar(_: i8) { }
|
||||||
have type `U`.
|
|
||||||
|
|
||||||
* function results (either the final line of a block if it is not semicolon
|
fn main() {
|
||||||
terminated or any expression in a `return` statement).
|
bar(128);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
In `fn foo() -> U { e }`, `e` is coerced to to have type `U`.
|
* Instantiations of struct or variant fields
|
||||||
|
|
||||||
|
For example, `128` is coerced to have type `i8` in the following:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
struct Foo { x: i8 }
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
Foo { x: 128 };
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
* Function results, either the final line of a block if it is not
|
||||||
|
semicolon-terminated or any expression in a `return` statement
|
||||||
|
|
||||||
|
For example, `128` is coerced to have type `i8` in the following:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn foo() -> i8 {
|
||||||
|
128
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
If the expression in one of these coercion sites is a coercion-propagating
|
If the expression in one of these coercion sites is a coercion-propagating
|
||||||
expression, then the relevant sub-expressions in that expression are also
|
expression, then the relevant sub-expressions in that expression are also
|
||||||
coercion sites. Propagation recurses from these new coercion sites.
|
coercion sites. Propagation recurses from these new coercion sites.
|
||||||
Propagating expressions and their relevant sub-expressions are:
|
Propagating expressions and their relevant sub-expressions are:
|
||||||
|
|
||||||
* array literals, where the array has type `[U; n]`. Each sub-expression in
|
* Array literals, where the array has type `[U; n]`. Each sub-expression in
|
||||||
the array literal is a coercion site for coercion to type `U`.
|
the array literal is a coercion site for coercion to type `U`.
|
||||||
|
|
||||||
* array literals with repeating syntax, where the array has type `[U; n]`. The
|
* Array literals with repeating syntax, where the array has type `[U; n]`. The
|
||||||
repeated sub-expression is a coercion site for coercion to type `U`.
|
repeated sub-expression is a coercion site for coercion to type `U`.
|
||||||
|
|
||||||
* tuples, where a tuple is a coercion site to type `(U_0, U_1, ..., U_n)`.
|
* Tuples, where a tuple is a coercion site to type `(U_0, U_1, ..., U_n)`.
|
||||||
Each sub-expression is a coercion site to the respective type, e.g. the
|
Each sub-expression is a coercion site to the respective type, e.g. the
|
||||||
zeroth sub-expression is a coercion site to type `U_0`.
|
zeroth sub-expression is a coercion site to type `U_0`.
|
||||||
|
|
||||||
* parenthesised sub-expressions (`(e)`). If the expression has type `U`, then
|
* Parenthesised sub-expressions (`(e)`): if the expression has type `U`, then
|
||||||
the sub-expression is a coercion site to `U`.
|
the sub-expression is a coercion site to `U`.
|
||||||
|
|
||||||
* blocks. If a block has type `U`, then the last expression in the block (if
|
* Blocks: if a block has type `U`, then the last expression in the block (if
|
||||||
it is not semicolon-terminated) is a coercion site to `U`. This includes
|
it is not semicolon-terminated) is a coercion site to `U`. This includes
|
||||||
blocks which are part of control flow statements, such as `if`/`else`, if
|
blocks which are part of control flow statements, such as `if`/`else`, if
|
||||||
the block has a known type.
|
the block has a known type.
|
||||||
@ -3701,45 +3741,46 @@ the block has a known type.
|
|||||||
|
|
||||||
Coercion is allowed between the following types:
|
Coercion is allowed between the following types:
|
||||||
|
|
||||||
* `T` to `U` if `T` is a subtype of `U` (*reflexive case*).
|
* `T` to `U` if `T` is a subtype of `U` (*reflexive case*)
|
||||||
|
|
||||||
* `T_1` to `T_3` where `T_1` coerces to `T_2` and `T_2` coerces to `T_3`
|
* `T_1` to `T_3` where `T_1` coerces to `T_2` and `T_2` coerces to `T_3`
|
||||||
(*transitive case*).
|
(*transitive case*)
|
||||||
|
|
||||||
Note that this is not fully supported yet
|
Note that this is not fully supported yet
|
||||||
|
|
||||||
* `&mut T` to `&T`.
|
* `&mut T` to `&T`
|
||||||
|
|
||||||
* `*mut T` to `*const T`.
|
* `*mut T` to `*const T`
|
||||||
|
|
||||||
* `&T` to `*const T`.
|
* `&T` to `*const T`
|
||||||
|
|
||||||
* `&mut T` to `*mut T`.
|
* `&mut T` to `*mut T`
|
||||||
|
|
||||||
* `&T` to `&U` if `T` implements `Deref<Target = U>`. For example:
|
* `&T` to `&U` if `T` implements `Deref<Target = U>`. For example:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
|
||||||
struct CharContainer {
|
struct CharContainer {
|
||||||
value: char
|
value: char
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deref for CharContainer {
|
impl Deref for CharContainer {
|
||||||
type Target = char;
|
type Target = char;
|
||||||
|
|
||||||
fn deref<'a>(&'a self) -> &'a char {
|
fn deref<'a>(&'a self) -> &'a char {
|
||||||
&self.value
|
&self.value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn foo(arg: &char) {}
|
fn foo(arg: &char) {}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let x = &mut CharContainer { value: 'y' };
|
||||||
|
foo(x); //&mut CharContainer is coerced to &char.
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let x = &mut CharContainer { value: 'y' };
|
|
||||||
foo(x); //&mut CharContainer is coerced to &char.
|
|
||||||
}
|
|
||||||
```
|
|
||||||
* `&mut T` to `&mut U` if `T` implements `DerefMut<Target = U>`.
|
* `&mut T` to `&mut U` if `T` implements `DerefMut<Target = U>`.
|
||||||
|
|
||||||
* TyCtor(`T`) to TyCtor(coerce_inner(`T`)), where TyCtor(`T`) is one of
|
* TyCtor(`T`) to TyCtor(coerce_inner(`T`)), where TyCtor(`T`) is one of
|
||||||
@ -3953,7 +3994,7 @@ In general, `--crate-type=bin` or `--crate-type=lib` should be sufficient for
|
|||||||
all compilation needs, and the other options are just available if more
|
all compilation needs, and the other options are just available if more
|
||||||
fine-grained control is desired over the output format of a Rust crate.
|
fine-grained control is desired over the output format of a Rust crate.
|
||||||
|
|
||||||
# Appendix: Rationales and design tradeoffs
|
# Appendix: Rationales and design trade-offs
|
||||||
|
|
||||||
*TODO*.
|
*TODO*.
|
||||||
|
|
||||||
@ -3963,7 +4004,7 @@ Rust is not a particularly original language, with design elements coming from
|
|||||||
a wide range of sources. Some of these are listed below (including elements
|
a wide range of sources. Some of these are listed below (including elements
|
||||||
that have since been removed):
|
that have since been removed):
|
||||||
|
|
||||||
* SML, OCaml: algebraic datatypes, pattern matching, type inference,
|
* SML, OCaml: algebraic data types, pattern matching, type inference,
|
||||||
semicolon statement separation
|
semicolon statement separation
|
||||||
* C++: references, RAII, smart pointers, move semantics, monomorphisation,
|
* C++: references, RAII, smart pointers, move semantics, monomorphisation,
|
||||||
memory model
|
memory model
|
||||||
|
@ -221,6 +221,10 @@ a > code {
|
|||||||
color: #428BCA;
|
color: #428BCA;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.section-header > a > code {
|
||||||
|
color: #8D1A38;
|
||||||
|
}
|
||||||
|
|
||||||
/* Code highlighting */
|
/* Code highlighting */
|
||||||
pre.rust .kw { color: #8959A8; }
|
pre.rust .kw { color: #8959A8; }
|
||||||
pre.rust .kw-2, pre.rust .prelude-ty { color: #4271AE; }
|
pre.rust .kw-2, pre.rust .prelude-ty { color: #4271AE; }
|
||||||
|
@ -29,7 +29,7 @@ explicitly implement to be used by this generic function.
|
|||||||
explicit conversions or other method calls would usually be necessary. See the
|
explicit conversions or other method calls would usually be necessary. See the
|
||||||
[overloading/implicits use case](#use-case:-limited-overloading-and/or-implicit-conversions)
|
[overloading/implicits use case](#use-case:-limited-overloading-and/or-implicit-conversions)
|
||||||
below.
|
below.
|
||||||
* _Precise types_. Because generic give a _name_ to the specific type
|
* _Precise types_. Because generics give a _name_ to the specific type
|
||||||
implementing a trait, it is possible to be precise about places where that
|
implementing a trait, it is possible to be precise about places where that
|
||||||
exact type is required or produced. For example, a function
|
exact type is required or produced. For example, a function
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
* [Iterators](iterators.md)
|
* [Iterators](iterators.md)
|
||||||
* [Concurrency](concurrency.md)
|
* [Concurrency](concurrency.md)
|
||||||
* [Error Handling](error-handling.md)
|
* [Error Handling](error-handling.md)
|
||||||
|
* [Choosing your Guarantees](choosing-your-guarantees.md)
|
||||||
* [FFI](ffi.md)
|
* [FFI](ffi.md)
|
||||||
* [Borrow and AsRef](borrow-and-asref.md)
|
* [Borrow and AsRef](borrow-and-asref.md)
|
||||||
* [Release Channels](release-channels.md)
|
* [Release Channels](release-channels.md)
|
||||||
@ -25,8 +26,7 @@
|
|||||||
* [Primitive Types](primitive-types.md)
|
* [Primitive Types](primitive-types.md)
|
||||||
* [Comments](comments.md)
|
* [Comments](comments.md)
|
||||||
* [if](if.md)
|
* [if](if.md)
|
||||||
* [for loops](for-loops.md)
|
* [Loops](loops.md)
|
||||||
* [while loops](while-loops.md)
|
|
||||||
* [Ownership](ownership.md)
|
* [Ownership](ownership.md)
|
||||||
* [References and Borrowing](references-and-borrowing.md)
|
* [References and Borrowing](references-and-borrowing.md)
|
||||||
* [Lifetimes](lifetimes.md)
|
* [Lifetimes](lifetimes.md)
|
||||||
@ -63,7 +63,7 @@
|
|||||||
* [No stdlib](no-stdlib.md)
|
* [No stdlib](no-stdlib.md)
|
||||||
* [Intrinsics](intrinsics.md)
|
* [Intrinsics](intrinsics.md)
|
||||||
* [Lang items](lang-items.md)
|
* [Lang items](lang-items.md)
|
||||||
* [Link args](link-args.md)
|
* [Advanced linking](advanced-linking.md)
|
||||||
* [Benchmark Tests](benchmark-tests.md)
|
* [Benchmark Tests](benchmark-tests.md)
|
||||||
* [Box Syntax and Patterns](box-syntax-and-patterns.md)
|
* [Box Syntax and Patterns](box-syntax-and-patterns.md)
|
||||||
* [Slice Patterns](slice-patterns.md)
|
* [Slice Patterns](slice-patterns.md)
|
||||||
|
@ -12,7 +12,7 @@ Recommended for inspiration and a better understanding of Rust's background.
|
|||||||
* [Macros that work together](https://www.cs.utah.edu/plt/publications/jfp12-draft-fcdf.pdf)
|
* [Macros that work together](https://www.cs.utah.edu/plt/publications/jfp12-draft-fcdf.pdf)
|
||||||
* [Traits: composable units of behavior](http://scg.unibe.ch/archive/papers/Scha03aTraits.pdf)
|
* [Traits: composable units of behavior](http://scg.unibe.ch/archive/papers/Scha03aTraits.pdf)
|
||||||
* [Alias burying](http://www.cs.uwm.edu/faculty/boyland/papers/unique-preprint.ps) - We tried something similar and abandoned it.
|
* [Alias burying](http://www.cs.uwm.edu/faculty/boyland/papers/unique-preprint.ps) - We tried something similar and abandoned it.
|
||||||
* [External uniqueness is unique enough](http://www.computingscience.nl/research/techreps/repo/CS-2002/2002-048.pdf)
|
* [External uniqueness is unique enough](http://www.cs.uu.nl/research/techreps/UU-CS-2002-048.html)
|
||||||
* [Uniqueness and Reference Immutability for Safe Parallelism](https://research.microsoft.com/pubs/170528/msr-tr-2012-79.pdf)
|
* [Uniqueness and Reference Immutability for Safe Parallelism](https://research.microsoft.com/pubs/170528/msr-tr-2012-79.pdf)
|
||||||
* [Region Based Memory Management](http://www.cs.ucla.edu/~palsberg/tba/papers/tofte-talpin-iandc97.pdf)
|
* [Region Based Memory Management](http://www.cs.ucla.edu/~palsberg/tba/papers/tofte-talpin-iandc97.pdf)
|
||||||
|
|
||||||
@ -26,10 +26,10 @@ Recommended for inspiration and a better understanding of Rust's background.
|
|||||||
* [Dynamic circular work stealing deque](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.170.1097&rep=rep1&type=pdf) - The Chase/Lev deque
|
* [Dynamic circular work stealing deque](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.170.1097&rep=rep1&type=pdf) - The Chase/Lev deque
|
||||||
* [Work-first and help-first scheduling policies for async-finish task parallelism](http://www.cs.rice.edu/%7Eyguo/pubs/PID824943.pdf) - More general than fully-strict work stealing
|
* [Work-first and help-first scheduling policies for async-finish task parallelism](http://www.cs.rice.edu/%7Eyguo/pubs/PID824943.pdf) - More general than fully-strict work stealing
|
||||||
* [A Java fork/join calamity](http://www.coopsoft.com/ar/CalamityArticle.html) - critique of Java's fork/join library, particularly its application of work stealing to non-strict computation
|
* [A Java fork/join calamity](http://www.coopsoft.com/ar/CalamityArticle.html) - critique of Java's fork/join library, particularly its application of work stealing to non-strict computation
|
||||||
* [Scheduling techniques for concurrent systems](http://www.ece.rutgers.edu/%7Eparashar/Classes/ece572-papers/05/ps-ousterhout.pdf)
|
* [Scheduling techniques for concurrent systems](http://www.stanford.edu/~ouster/cgi-bin/papers/coscheduling.pdf)
|
||||||
* [Contention aware scheduling](http://www.blagodurov.net/files/a8-blagodurov.pdf)
|
* [Contention aware scheduling](http://www.blagodurov.net/files/a8-blagodurov.pdf)
|
||||||
* [Balanced work stealing for time-sharing multicores](http://www.cse.ohio-state.edu/hpcs/WWW/HTML/publications/papers/TR-12-1.pdf)
|
* [Balanced work stealing for time-sharing multicores](http://www.cse.ohio-state.edu/hpcs/WWW/HTML/publications/papers/TR-12-1.pdf)
|
||||||
* [Three layer cake](http://www.upcrc.illinois.edu/workshops/paraplop10/papers/paraplop10_submission_8.pdf)
|
* [Three layer cake for shared-memory programming](http://dl.acm.org/citation.cfm?id=1953616&dl=ACM&coll=DL&CFID=524387192&CFTOKEN=44362705)
|
||||||
* [Non-blocking steal-half work queues](http://www.cs.bgu.ac.il/%7Ehendlerd/papers/p280-hendler.pdf)
|
* [Non-blocking steal-half work queues](http://www.cs.bgu.ac.il/%7Ehendlerd/papers/p280-hendler.pdf)
|
||||||
* [Reagents: expressing and composing fine-grained concurrency](http://www.mpi-sws.org/~turon/reagents.pdf)
|
* [Reagents: expressing and composing fine-grained concurrency](http://www.mpi-sws.org/~turon/reagents.pdf)
|
||||||
* [Algorithms for scalable synchronization of shared-memory multiprocessors](https://www.cs.rochester.edu/u/scott/papers/1991_TOCS_synch.pdf)
|
* [Algorithms for scalable synchronization of shared-memory multiprocessors](https://www.cs.rochester.edu/u/scott/papers/1991_TOCS_synch.pdf)
|
||||||
@ -42,5 +42,32 @@ Recommended for inspiration and a better understanding of Rust's background.
|
|||||||
|
|
||||||
### Papers *about* Rust
|
### Papers *about* Rust
|
||||||
|
|
||||||
* [GPU programming in Rust](http://www.cs.indiana.edu/~eholk/papers/hips2013.pdf)
|
* [GPU Programming in Rust: Implementing High Level Abstractions in a
|
||||||
* [Parallel closures: a new twist on an old idea](https://www.usenix.org/conference/hotpar12/parallel-closures-new-twist-old-idea) - not exactly about rust, but by nmatsakis
|
Systems Level
|
||||||
|
Language](http://www.cs.indiana.edu/~eholk/papers/hips2013.pdf). Early GPU work by Eric Holk.
|
||||||
|
* [Parallel closures: a new twist on an old
|
||||||
|
idea](https://www.usenix.org/conference/hotpar12/parallel-closures-new-twist-old-idea)
|
||||||
|
- not exactly about rust, but by nmatsakis
|
||||||
|
* [Patina: A Formalization of the Rust Programming
|
||||||
|
Language](ftp://ftp.cs.washington.edu/tr/2015/03/UW-CSE-15-03-02.pdf). Early
|
||||||
|
formalization of a subset of the type system, by Eric Reed.
|
||||||
|
* [Experience Report: Developing the Servo Web Browser Engine using
|
||||||
|
Rust](http://arxiv.org/abs/1505.07383). By Lars Bergstrom.
|
||||||
|
* [Implementing a Generic Radix Trie in
|
||||||
|
Rust](https://michaelsproul.github.io/rust_radix_paper/rust-radix-sproul.pdf). Undergrad
|
||||||
|
paper by Michael Sproul.
|
||||||
|
* [Reenix: Implementing a Unix-Like Operating System in
|
||||||
|
Rust](http://scialex.github.io/reenix.pdf). Undergrad paper by Alex
|
||||||
|
Light.
|
||||||
|
* [Evaluation of performance and productivity metrics of potential
|
||||||
|
programming languages in the HPC environment](). Bachelor's thesis by
|
||||||
|
Florian Wilkens. Compares C, Go and Rust.
|
||||||
|
* [Nom, a byte oriented, streaming, zero copy, parser combinators library
|
||||||
|
in Rust](http://spw15.langsec.org/papers/couprie-nom.pdf). By
|
||||||
|
Geoffroy Couprie, research for VLC.
|
||||||
|
* [Graph-Based Higher-Order Intermediate
|
||||||
|
Representation](http://compilers.cs.uni-saarland.de/papers/lkh15_cgo.pdf). An
|
||||||
|
experimental IR implemented in Impala, a Rust-like language.
|
||||||
|
* [Code Refinement of Stencil
|
||||||
|
Codes](http://compilers.cs.uni-saarland.de/papers/ppl14_web.pdf). Another
|
||||||
|
paper using Impala.
|
||||||
|
151
src/doc/trpl/advanced-linking.md
Normal file
151
src/doc/trpl/advanced-linking.md
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
% Advanced Linking
|
||||||
|
|
||||||
|
The common cases of linking with Rust have been covered earlier in this book,
|
||||||
|
but supporting the range of linking possibilities made available by other
|
||||||
|
languages is important for Rust to achieve seamless interaction with native
|
||||||
|
libraries.
|
||||||
|
|
||||||
|
# Link args
|
||||||
|
|
||||||
|
There is one other way to tell `rustc` how to customize linking, and that is via
|
||||||
|
the `link_args` attribute. This attribute is applied to `extern` blocks and
|
||||||
|
specifies raw flags which need to get passed to the linker when producing an
|
||||||
|
artifact. An example usage would be:
|
||||||
|
|
||||||
|
``` no_run
|
||||||
|
#![feature(link_args)]
|
||||||
|
|
||||||
|
#[link_args = "-foo -bar -baz"]
|
||||||
|
extern {}
|
||||||
|
# fn main() {}
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that this feature is currently hidden behind the `feature(link_args)` gate
|
||||||
|
because this is not a sanctioned way of performing linking. Right now `rustc`
|
||||||
|
shells out to the system linker (`gcc` on most systems, `link.exe` on MSVC),
|
||||||
|
so it makes sense to provide extra command line
|
||||||
|
arguments, but this will not always be the case. In the future `rustc` may use
|
||||||
|
LLVM directly to link native libraries, in which case `link_args` will have no
|
||||||
|
meaning. You can achieve the same effect as the `link-args` attribute with the
|
||||||
|
`-C link-args` argument to `rustc`.
|
||||||
|
|
||||||
|
It is highly recommended to *not* use this attribute, and rather use the more
|
||||||
|
formal `#[link(...)]` attribute on `extern` blocks instead.
|
||||||
|
|
||||||
|
# Static linking
|
||||||
|
|
||||||
|
Static linking refers to the process of creating output that contain all
|
||||||
|
required libraries and so don't need libraries installed on every system where
|
||||||
|
you want to use your compiled project. Pure-Rust dependencies are statically
|
||||||
|
linked by default so you can use created binaries and libraries without
|
||||||
|
installing the Rust everywhere. By contrast, native libraries
|
||||||
|
(e.g. `libc` and `libm`) usually dynamically linked, but it is possible to
|
||||||
|
change this and statically link them as well.
|
||||||
|
|
||||||
|
Linking is a very platform dependent topic — on some platforms, static linking
|
||||||
|
may not be possible at all! This section assumes some basic familiarity with
|
||||||
|
linking on your platform of choice.
|
||||||
|
|
||||||
|
## Linux
|
||||||
|
|
||||||
|
By default, all Rust programs on Linux will link to the system `libc` along with
|
||||||
|
a number of other libraries. Let's look at an example on a 64-bit Linux machine
|
||||||
|
with GCC and `glibc` (by far the most common `libc` on Linux):
|
||||||
|
|
||||||
|
``` text
|
||||||
|
$ cat example.rs
|
||||||
|
fn main() {}
|
||||||
|
$ rustc example.rs
|
||||||
|
$ ldd example
|
||||||
|
linux-vdso.so.1 => (0x00007ffd565fd000)
|
||||||
|
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fa81889c000)
|
||||||
|
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fa81867e000)
|
||||||
|
librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007fa818475000)
|
||||||
|
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007fa81825f000)
|
||||||
|
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fa817e9a000)
|
||||||
|
/lib64/ld-linux-x86-64.so.2 (0x00007fa818cf9000)
|
||||||
|
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fa817b93000)
|
||||||
|
```
|
||||||
|
|
||||||
|
Dynamic linking on Linux can be undesirable if you wish to use new library
|
||||||
|
features on old systems or target systems which do not have the required
|
||||||
|
dependencies for your program to run.
|
||||||
|
|
||||||
|
Static linking is supported via an alternative `libc`, `musl` - this must be
|
||||||
|
enabled at Rust compile-time with some prerequisites available. You can compile
|
||||||
|
your own version of Rust with `musl` enabled and install it into a custom
|
||||||
|
directory with the instructions below:
|
||||||
|
|
||||||
|
```text
|
||||||
|
$ mkdir musldist
|
||||||
|
$ PREFIX=$(pwd)/musldist
|
||||||
|
$
|
||||||
|
$ # Build musl
|
||||||
|
$ wget http://www.musl-libc.org/releases/musl-1.1.10.tar.gz
|
||||||
|
[...]
|
||||||
|
$ tar xf musl-1.1.10.tar.gz
|
||||||
|
$ cd musl-1.1.10/
|
||||||
|
musl-1.1.10 $ ./configure --disable-shared --prefix=$PREFIX
|
||||||
|
[...]
|
||||||
|
musl-1.1.10 $ make
|
||||||
|
[...]
|
||||||
|
musl-1.1.10 $ make install
|
||||||
|
[...]
|
||||||
|
musl-1.1.10 $ cd ..
|
||||||
|
$ du -h musldist/lib/libc.a
|
||||||
|
2.2M musldist/lib/libc.a
|
||||||
|
$
|
||||||
|
$ # Build libunwind.a
|
||||||
|
$ wget http://llvm.org/releases/3.6.1/llvm-3.6.1.src.tar.xz
|
||||||
|
$ tar xf llvm-3.6.1.src.tar.xz
|
||||||
|
$ cd llvm-3.6.1.src/projects/
|
||||||
|
llvm-3.6.1.src/projects $ svn co http://llvm.org/svn/llvm-project/libcxxabi/trunk/ libcxxabi
|
||||||
|
llvm-3.6.1.src/projects $ svn co http://llvm.org/svn/llvm-project/libunwind/trunk/ libunwind
|
||||||
|
llvm-3.6.1.src/projects $ sed -i 's#^\(include_directories\).*$#\0\n\1(../libcxxabi/include)#' libunwind/CMakeLists.txt
|
||||||
|
llvm-3.6.1.src/projects $ mkdir libunwind/build
|
||||||
|
llvm-3.6.1.src/projects $ cd libunwind/build
|
||||||
|
llvm-3.6.1.src/projects/libunwind/build $ cmake -DLLVM_PATH=../../.. -DLIBUNWIND_ENABLE_SHARED=0 ..
|
||||||
|
llvm-3.6.1.src/projects/libunwind/build $ make
|
||||||
|
llvm-3.6.1.src/projects/libunwind/build $ cp lib/libunwind.a $PREFIX/lib/
|
||||||
|
llvm-3.6.1.src/projects/libunwind/build $ cd cd ../../../../
|
||||||
|
$ du -h musldist/lib/libunwind.a
|
||||||
|
164K musldist/lib/libunwind.a
|
||||||
|
$
|
||||||
|
$ # Build musl-enabled rust
|
||||||
|
$ git clone https://github.com/rust-lang/rust.git muslrust
|
||||||
|
$ cd muslrust
|
||||||
|
muslrust $ ./configure --target=x86_64-unknown-linux-musl --musl-root=$PREFIX --prefix=$PREFIX
|
||||||
|
muslrust $ make
|
||||||
|
muslrust $ make install
|
||||||
|
muslrust $ cd ..
|
||||||
|
$ du -h musldist/bin/rustc
|
||||||
|
12K musldist/bin/rustc
|
||||||
|
```
|
||||||
|
|
||||||
|
You now have a build of a `musl`-enabled Rust! Because we've installed it to a
|
||||||
|
custom prefix we need to make sure our system can the binaries and appropriate
|
||||||
|
libraries when we try and run it:
|
||||||
|
|
||||||
|
```text
|
||||||
|
$ export PATH=$PREFIX/bin:$PATH
|
||||||
|
$ export LD_LIBRARY_PATH=$PREFIX/lib:$LD_LIBRARY_PATH
|
||||||
|
```
|
||||||
|
|
||||||
|
Let's try it out!
|
||||||
|
|
||||||
|
```text
|
||||||
|
$ echo 'fn main() { println!("hi!"); panic!("failed"); }' > example.rs
|
||||||
|
$ rustc --target=x86_64-unknown-linux-musl example.rs
|
||||||
|
$ ldd example
|
||||||
|
not a dynamic executable
|
||||||
|
$ ./example
|
||||||
|
hi!
|
||||||
|
thread '<main>' panicked at 'failed', example.rs:1
|
||||||
|
```
|
||||||
|
|
||||||
|
Success! This binary can be copied to almost any Linux machine with the same
|
||||||
|
machine architecture and run without issues.
|
||||||
|
|
||||||
|
`cargo build` also permits the `--target` option so you should be able to build
|
||||||
|
your crates as normal. However, you may need to recompile your native libraries
|
||||||
|
against `musl` before they can be linked against.
|
359
src/doc/trpl/choosing-your-guarantees.md
Normal file
359
src/doc/trpl/choosing-your-guarantees.md
Normal file
@ -0,0 +1,359 @@
|
|||||||
|
% Choosing your Guarantees
|
||||||
|
|
||||||
|
One important feature of Rust as language is that it lets us control the costs and guarantees
|
||||||
|
of a program.
|
||||||
|
|
||||||
|
There are various “wrapper type” abstractions in the Rust standard library which embody
|
||||||
|
a multitude of tradeoffs between cost, ergonomics, and guarantees. Many let one choose between
|
||||||
|
run time and compile time enforcement. This section will explain a few selected abstractions in
|
||||||
|
detail.
|
||||||
|
|
||||||
|
Before proceeding, it is highly recommended that one reads about [ownership][ownership] and
|
||||||
|
[borrowing][borrowing] in Rust.
|
||||||
|
|
||||||
|
[ownership]: ownership.html
|
||||||
|
[borrowing]: references-and-borrowing.html
|
||||||
|
|
||||||
|
# Basic pointer types
|
||||||
|
|
||||||
|
## `Box<T>`
|
||||||
|
|
||||||
|
[`Box<T>`][box] is pointer which is “owned”, or a “box”. While it can hand
|
||||||
|
out references to the contained data, it is the only owner of the data. In particular, when
|
||||||
|
something like the following occurs:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let x = Box::new(1);
|
||||||
|
let y = x;
|
||||||
|
// x no longer accessible here
|
||||||
|
```
|
||||||
|
|
||||||
|
Here, the box was _moved_ into `y`. As `x` no longer owns it, the compiler will no longer allow the
|
||||||
|
programmer to use `x` after this. A box can similarly be moved _out_ of a function by returning it.
|
||||||
|
|
||||||
|
When a box (that hasn't been moved) goes out of scope, destructors are run. These destructors take
|
||||||
|
care of deallocating the inner data.
|
||||||
|
|
||||||
|
This is a zero-cost abstraction for dynamic allocation. If you want to allocate some memory on the
|
||||||
|
heap and safely pass around a pointer to that memory, this is ideal. Note that you will only be
|
||||||
|
allowed to share references to this by the regular borrowing rules, checked at compile time.
|
||||||
|
|
||||||
|
[box]: ../std/boxed/struct.Box.html
|
||||||
|
|
||||||
|
## `&T` and `&mut T`
|
||||||
|
|
||||||
|
These are immutable and mutable references respectively. They follow the “read-write lock”
|
||||||
|
pattern, such that one may either have only one mutable reference to some data, or any number of
|
||||||
|
immutable ones, but not both. This guarantee is enforced at compile time, and has no visible cost at
|
||||||
|
runtime. In most cases these two pointer types suffice for sharing cheap references between sections
|
||||||
|
of code.
|
||||||
|
|
||||||
|
These pointers cannot be copied in such a way that they outlive the lifetime associated with them.
|
||||||
|
|
||||||
|
## `*const T` and `*mut T`
|
||||||
|
|
||||||
|
These are C-like raw pointers with no lifetime or ownership attached to them. They just point to
|
||||||
|
some location in memory with no other restrictions. The only guarantee that these provide is that
|
||||||
|
they cannot be dereferenced except in code marked `unsafe`.
|
||||||
|
|
||||||
|
These are useful when building safe, low cost abstractions like `Vec<T>`, but should be avoided in
|
||||||
|
safe code.
|
||||||
|
|
||||||
|
## `Rc<T>`
|
||||||
|
|
||||||
|
This is the first wrapper we will cover that has a runtime cost.
|
||||||
|
|
||||||
|
[`Rc<T>`][rc] is a reference counted pointer. In other words, this lets us have multiple "owning"
|
||||||
|
pointers to the same data, and the data will be dropped (destructors will be run) when all pointers
|
||||||
|
are out of scope.
|
||||||
|
|
||||||
|
Internally, it contains a shared “reference count” (also called “refcount”),
|
||||||
|
which is incremented each time the `Rc` is cloned, and decremented each time one of the `Rc`s goes
|
||||||
|
out of scope. The main responsibility of `Rc<T>` is to ensure that destructors are called for shared
|
||||||
|
data.
|
||||||
|
|
||||||
|
The internal data here is immutable, and if a cycle of references is created, the data will be
|
||||||
|
leaked. If we want data that doesn't leak when there are cycles, we need a garbage collector.
|
||||||
|
|
||||||
|
#### Guarantees
|
||||||
|
|
||||||
|
The main guarantee provided here is that the data will not be destroyed until all references to it
|
||||||
|
are out of scope.
|
||||||
|
|
||||||
|
This should be used when we wish to dynamically allocate and share some data (read-only) between
|
||||||
|
various portions of your program, where it is not certain which portion will finish using the pointer
|
||||||
|
last. It's a viable alternative to `&T` when `&T` is either impossible to statically check for
|
||||||
|
correctness, or creates extremely unergonomic code where the programmer does not wish to spend the
|
||||||
|
development cost of working with.
|
||||||
|
|
||||||
|
This pointer is _not_ thread safe, and Rust will not let it be sent or shared with other threads.
|
||||||
|
This lets one avoid the cost of atomics in situations where they are unnecessary.
|
||||||
|
|
||||||
|
There is a sister smart pointer to this one, `Weak<T>`. This is a non-owning, but also non-borrowed,
|
||||||
|
smart pointer. It is also similar to `&T`, but it is not restricted in lifetime—a `Weak<T>`
|
||||||
|
can be held on to forever. However, it is possible that an attempt to access the inner data may fail
|
||||||
|
and return `None`, since this can outlive the owned `Rc`s. This is useful for cyclic
|
||||||
|
data structures and other things.
|
||||||
|
|
||||||
|
#### Cost
|
||||||
|
|
||||||
|
As far as memory goes, `Rc<T>` is a single allocation, though it will allocate two extra words (i.e.
|
||||||
|
two `usize` values) as compared to a regular `Box<T>` (for "strong" and "weak" refcounts).
|
||||||
|
|
||||||
|
`Rc<T>` has the computational cost of incrementing/decrementing the refcount whenever it is cloned
|
||||||
|
or goes out of scope respectively. Note that a clone will not do a deep copy, rather it will simply
|
||||||
|
increment the inner reference count and return a copy of the `Rc<T>`.
|
||||||
|
|
||||||
|
[rc]: ../std/rc/struct.Rc.html
|
||||||
|
|
||||||
|
# Cell types
|
||||||
|
|
||||||
|
`Cell`s provide interior mutability. In other words, they contain data which can be manipulated even
|
||||||
|
if the type cannot be obtained in a mutable form (for example, when it is behind an `&`-ptr or
|
||||||
|
`Rc<T>`).
|
||||||
|
|
||||||
|
[The documentation for the `cell` module has a pretty good explanation for these][cell-mod].
|
||||||
|
|
||||||
|
These types are _generally_ found in struct fields, but they may be found elsewhere too.
|
||||||
|
|
||||||
|
## `Cell<T>`
|
||||||
|
|
||||||
|
[`Cell<T>`][cell] is a type that provides zero-cost interior mutability, but only for `Copy` types.
|
||||||
|
Since the compiler knows that all the data owned by the contained value is on the stack, there's
|
||||||
|
no worry of leaking any data behind references (or worse!) by simply replacing the data.
|
||||||
|
|
||||||
|
It is still possible to violate your own invariants using this wrapper, so be careful when using it.
|
||||||
|
If a field is wrapped in `Cell`, it's a nice indicator that the chunk of data is mutable and may not
|
||||||
|
stay the same between the time you first read it and when you intend to use it.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use std::cell::Cell;
|
||||||
|
|
||||||
|
let x = Cell::new(1);
|
||||||
|
let y = &x;
|
||||||
|
let z = &x;
|
||||||
|
x.set(2);
|
||||||
|
y.set(3);
|
||||||
|
z.set(4);
|
||||||
|
println!("{}", x.get());
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that here we were able to mutate the same value from various immutable references.
|
||||||
|
|
||||||
|
This has the same runtime cost as the following:
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
let mut x = 1;
|
||||||
|
let y = &mut x;
|
||||||
|
let z = &mut x;
|
||||||
|
x = 2;
|
||||||
|
*y = 3;
|
||||||
|
*z = 4;
|
||||||
|
println!("{}", x);
|
||||||
|
```
|
||||||
|
|
||||||
|
but it has the added benefit of actually compiling successfully.
|
||||||
|
|
||||||
|
#### Guarantees
|
||||||
|
|
||||||
|
This relaxes the “no aliasing with mutability” restriction in places where it's
|
||||||
|
unnecessary. However, this also relaxes the guarantees that the restriction provides; so if your
|
||||||
|
invariants depend on data stored within `Cell`, you should be careful.
|
||||||
|
|
||||||
|
This is useful for mutating primitives and other `Copy` types when there is no easy way of
|
||||||
|
doing it in line with the static rules of `&` and `&mut`.
|
||||||
|
|
||||||
|
`Cell` does not let you obtain interior references to the data, which makes it safe to freely
|
||||||
|
mutate.
|
||||||
|
|
||||||
|
#### Cost
|
||||||
|
|
||||||
|
There is no runtime cost to using `Cell<T>`, however if you are using it to wrap larger (`Copy`)
|
||||||
|
structs, it might be worthwhile to instead wrap individual fields in `Cell<T>` since each write is
|
||||||
|
otherwise a full copy of the struct.
|
||||||
|
|
||||||
|
|
||||||
|
## `RefCell<T>`
|
||||||
|
|
||||||
|
[`RefCell<T>`][refcell] also provides interior mutability, but isn't restricted to `Copy` types.
|
||||||
|
|
||||||
|
Instead, it has a runtime cost. `RefCell<T>` enforces the read-write lock pattern at runtime (it's
|
||||||
|
like a single-threaded mutex), unlike `&T`/`&mut T` which do so at compile time. This is done by the
|
||||||
|
`borrow()` and `borrow_mut()` functions, which modify an internal reference count and return smart
|
||||||
|
pointers which can be dereferenced immutably and mutably respectively. The refcount is restored when
|
||||||
|
the smart pointers go out of scope. With this system, we can dynamically ensure that there are never
|
||||||
|
any other borrows active when a mutable borrow is active. If the programmer attempts to make such a
|
||||||
|
borrow, the thread will panic.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use std::cell::RefCell;
|
||||||
|
|
||||||
|
let x = RefCell::new(vec![1,2,3,4]);
|
||||||
|
{
|
||||||
|
println!("{:?}", *x.borrow())
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut my_ref = x.borrow_mut();
|
||||||
|
my_ref.push(1);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Similar to `Cell`, this is mainly useful for situations where it's hard or impossible to satisfy the
|
||||||
|
borrow checker. Generally we know that such mutations won't happen in a nested form, but it's good
|
||||||
|
to check.
|
||||||
|
|
||||||
|
For large, complicated programs, it becomes useful to put some things in `RefCell`s to make things
|
||||||
|
simpler. For example, a lot of the maps in [the `ctxt` struct][ctxt] in the rust compiler internals
|
||||||
|
are inside this wrapper. These are only modified once (during creation, which is not right after
|
||||||
|
initialization) or a couple of times in well-separated places. However, since this struct is
|
||||||
|
pervasively used everywhere, juggling mutable and immutable pointers would be hard (perhaps
|
||||||
|
impossible) and probably form a soup of `&`-ptrs which would be hard to extend. On the other hand,
|
||||||
|
the `RefCell` provides a cheap (not zero-cost) way of safely accessing these. In the future, if
|
||||||
|
someone adds some code that attempts to modify the cell when it's already borrowed, it will cause a
|
||||||
|
(usually deterministic) panic which can be traced back to the offending borrow.
|
||||||
|
|
||||||
|
Similarly, in Servo's DOM there is a lot of mutation, most of which is local to a DOM type, but some
|
||||||
|
of which crisscrosses the DOM and modifies various things. Using `RefCell` and `Cell` to guard all
|
||||||
|
mutation lets us avoid worrying about mutability everywhere, and it simultaneously highlights the
|
||||||
|
places where mutation is _actually_ happening.
|
||||||
|
|
||||||
|
Note that `RefCell` should be avoided if a mostly simple solution is possible with `&` pointers.
|
||||||
|
|
||||||
|
#### Guarantees
|
||||||
|
|
||||||
|
`RefCell` relaxes the _static_ restrictions preventing aliased mutation, and replaces them with
|
||||||
|
_dynamic_ ones. As such the guarantees have not changed.
|
||||||
|
|
||||||
|
#### Cost
|
||||||
|
|
||||||
|
`RefCell` does not allocate, but it contains an additional "borrow state"
|
||||||
|
indicator (one word in size) along with the data.
|
||||||
|
|
||||||
|
At runtime each borrow causes a modification/check of the refcount.
|
||||||
|
|
||||||
|
[cell-mod]: ../std/cell/
|
||||||
|
[cell]: ../std/cell/struct.Cell.html
|
||||||
|
[refcell]: ../std/cell/struct.RefCell.html
|
||||||
|
[ctxt]: ../rustc/middle/ty/struct.ctxt.html
|
||||||
|
|
||||||
|
# Synchronous types
|
||||||
|
|
||||||
|
Many of the types above cannot be used in a threadsafe manner. Particularly, `Rc<T>` and
|
||||||
|
`RefCell<T>`, which both use non-atomic reference counts (_atomic_ reference counts are those which
|
||||||
|
can be incremented from multiple threads without causing a data race), cannot be used this way. This
|
||||||
|
makes them cheaper to use, but we need thread safe versions of these too. They exist, in the form of
|
||||||
|
`Arc<T>` and `Mutex<T>`/`RWLock<T>`
|
||||||
|
|
||||||
|
Note that the non-threadsafe types _cannot_ be sent between threads, and this is checked at compile
|
||||||
|
time.
|
||||||
|
|
||||||
|
There are many useful wrappers for concurrent programming in the [sync][sync] module, but only the
|
||||||
|
major ones will be covered below.
|
||||||
|
|
||||||
|
[sync]: ../std/sync/index.html
|
||||||
|
|
||||||
|
## `Arc<T>`
|
||||||
|
|
||||||
|
[`Arc<T>`][arc] is just a version of `Rc<T>` that uses an atomic reference count (hence, "Arc").
|
||||||
|
This can be sent freely between threads.
|
||||||
|
|
||||||
|
C++'s `shared_ptr` is similar to `Arc`, however in the case of C++ the inner data is always mutable.
|
||||||
|
For semantics similar to that from C++, we should use `Arc<Mutex<T>>`, `Arc<RwLock<T>>`, or
|
||||||
|
`Arc<UnsafeCell<T>>`[^4] (`UnsafeCell<T>` is a cell type that can be used to hold any data and has
|
||||||
|
no runtime cost, but accessing it requires `unsafe` blocks). The last one should only be used if we
|
||||||
|
are certain that the usage won't cause any memory unsafety. Remember that writing to a struct is not
|
||||||
|
an atomic operation, and many functions like `vec.push()` can reallocate internally and cause unsafe
|
||||||
|
behavior, so even monotonicity may not be enough to justify `UnsafeCell`.
|
||||||
|
|
||||||
|
[^4]: `Arc<UnsafeCell<T>>` actually won't compile since `UnsafeCell<T>` isn't `Send` or `Sync`, but we can wrap it in a type and implement `Send`/`Sync` for it manually to get `Arc<Wrapper<T>>` where `Wrapper` is `struct Wrapper<T>(UnsafeCell<T>)`.
|
||||||
|
|
||||||
|
#### Guarantees
|
||||||
|
|
||||||
|
Like `Rc`, this provides the (thread safe) guarantee that the destructor for the internal data will
|
||||||
|
be run when the last `Arc` goes out of scope (barring any cycles).
|
||||||
|
|
||||||
|
#### Cost
|
||||||
|
|
||||||
|
This has the added cost of using atomics for changing the refcount (which will happen whenever it is
|
||||||
|
cloned or goes out of scope). When sharing data from an `Arc` in a single thread, it is preferable
|
||||||
|
to share `&` pointers whenever possible.
|
||||||
|
|
||||||
|
[arc]: ../std/sync/struct.Arc.html
|
||||||
|
|
||||||
|
## `Mutex<T>` and `RwLock<T>`
|
||||||
|
|
||||||
|
[`Mutex<T>`][mutex] and [`RwLock<T>`][rwlock] provide mutual-exclusion via RAII guards (guards are
|
||||||
|
objects which maintain some state, like a lock, until their destructor is called). For both of
|
||||||
|
these, the mutex is opaque until we call `lock()` on it, at which point the thread will block
|
||||||
|
until a lock can be acquired, and then a guard will be returned. This guard can be used to access
|
||||||
|
the inner data (mutably), and the lock will be released when the guard goes out of scope.
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
{
|
||||||
|
let guard = mutex.lock();
|
||||||
|
// guard dereferences mutably to the inner type
|
||||||
|
*guard += 1;
|
||||||
|
} // lock released when destructor runs
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
`RwLock` has the added benefit of being efficient for multiple reads. It is always safe to have
|
||||||
|
multiple readers to shared data as long as there are no writers; and `RwLock` lets readers acquire a
|
||||||
|
"read lock". Such locks can be acquired concurrently and are kept track of via a reference count.
|
||||||
|
Writers must obtain a "write lock" which can only be obtained when all readers have gone out of
|
||||||
|
scope.
|
||||||
|
|
||||||
|
#### Guarantees
|
||||||
|
|
||||||
|
Both of these provide safe shared mutability across threads, however they are prone to deadlocks.
|
||||||
|
Some level of additional protocol safety can be obtained via the type system.
|
||||||
|
|
||||||
|
#### Costs
|
||||||
|
|
||||||
|
These use internal atomic-like types to maintain the locks, which are pretty costly (they can block
|
||||||
|
all memory reads across processors till they're done). Waiting on these locks can also be slow when
|
||||||
|
there's a lot of concurrent access happening.
|
||||||
|
|
||||||
|
[rwlock]: ../std/sync/struct.RwLock.html
|
||||||
|
[mutex]: ../std/sync/struct.Mutex.html
|
||||||
|
[sessions]: https://github.com/Munksgaard/rust-sessions
|
||||||
|
|
||||||
|
# Composition
|
||||||
|
|
||||||
|
A common gripe when reading Rust code is with types like `Rc<RefCell<Vec<T>>>` (or even more more
|
||||||
|
complicated compositions of such types). It's not always clear what the composition does, or why the
|
||||||
|
author chose one like this (and when one should be using such a composition in one's own code)
|
||||||
|
|
||||||
|
Usually, it's a case of composing together the guarantees that you need, without paying for stuff
|
||||||
|
that is unnecessary.
|
||||||
|
|
||||||
|
For example, `Rc<RefCell<T>>` is one such composition. `Rc<T>` itself can't be dereferenced mutably;
|
||||||
|
because `Rc<T>` provides sharing and shared mutability can lead to unsafe behavior, so we put
|
||||||
|
`RefCell<T>` inside to get dynamically verified shared mutability. Now we have shared mutable data,
|
||||||
|
but it's shared in a way that there can only be one mutator (and no readers) or multiple readers.
|
||||||
|
|
||||||
|
Now, we can take this a step further, and have `Rc<RefCell<Vec<T>>>` or `Rc<Vec<RefCell<T>>>`. These
|
||||||
|
are both shareable, mutable vectors, but they're not the same.
|
||||||
|
|
||||||
|
With the former, the `RefCell<T>` is wrapping the `Vec<T>`, so the `Vec<T>` in its entirety is
|
||||||
|
mutable. At the same time, there can only be one mutable borrow of the whole `Vec` at a given time.
|
||||||
|
This means that your code cannot simultaneously work on different elements of the vector from
|
||||||
|
different `Rc` handles. However, we are able to push and pop from the `Vec<T>` at will. This is
|
||||||
|
similar to an `&mut Vec<T>` with the borrow checking done at runtime.
|
||||||
|
|
||||||
|
With the latter, the borrowing is of individual elements, but the overall vector is immutable. Thus,
|
||||||
|
we can independently borrow separate elements, but we cannot push or pop from the vector. This is
|
||||||
|
similar to an `&mut [T]`[^3], but, again, the borrow checking is at runtime.
|
||||||
|
|
||||||
|
In concurrent programs, we have a similar situation with `Arc<Mutex<T>>`, which provides shared
|
||||||
|
mutability and ownership.
|
||||||
|
|
||||||
|
When reading code that uses these, go in step by step and look at the guarantees/costs provided.
|
||||||
|
|
||||||
|
When choosing a composed type, we must do the reverse; figure out which guarantees we want, and at
|
||||||
|
which point of the composition we need them. For example, if there is a choice between
|
||||||
|
`Vec<RefCell<T>>` and `RefCell<Vec<T>>`, we should figure out the tradeoffs as done above and pick
|
||||||
|
one.
|
||||||
|
|
||||||
|
[^3]: `&[T]` and `&mut [T]` are _slices_; they consist of a pointer and a length and can refer to a portion of a vector or array. `&mut [T]` can have its elements mutated, however its length cannot be touched.
|
@ -38,6 +38,17 @@ fn add_one(x: i32) -> i32 {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
There is another style of doc comment, `//!`, to comment containing items (e.g.
|
||||||
|
crates, modules or functions), instead of the items following it. Commonly used
|
||||||
|
inside crates root (lib.rs) or modules root (mod.rs):
|
||||||
|
|
||||||
|
```
|
||||||
|
//! # The Rust Standard Library
|
||||||
|
//!
|
||||||
|
//! The Rust Standard Library provides the essential runtime
|
||||||
|
//! functionality for building portable Rust software.
|
||||||
|
```
|
||||||
|
|
||||||
When writing doc comments, providing some examples of usage is very, very
|
When writing doc comments, providing some examples of usage is very, very
|
||||||
helpful. You’ll notice we’ve used a new macro here: `assert_eq!`. This compares
|
helpful. You’ll notice we’ve used a new macro here: `assert_eq!`. This compares
|
||||||
two values, and `panic!`s if they’re not equal to each other. It’s very helpful
|
two values, and `panic!`s if they’re not equal to each other. It’s very helpful
|
||||||
|
@ -61,7 +61,7 @@ fn expand_rn(cx: &mut ExtCtxt, sp: Span, args: &[TokenTree])
|
|||||||
("I", 1)];
|
("I", 1)];
|
||||||
|
|
||||||
let text = match args {
|
let text = match args {
|
||||||
[TtToken(_, token::Ident(s, _))] => token::get_ident(s).to_string(),
|
[TtToken(_, token::Ident(s, _))] => s.to_string(),
|
||||||
_ => {
|
_ => {
|
||||||
cx.span_err(sp, "argument should be a single identifier");
|
cx.span_err(sp, "argument should be a single identifier");
|
||||||
return DummyResult::any(sp);
|
return DummyResult::any(sp);
|
||||||
@ -186,8 +186,7 @@ impl LintPass for Pass {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn check_item(&mut self, cx: &Context, it: &ast::Item) {
|
fn check_item(&mut self, cx: &Context, it: &ast::Item) {
|
||||||
let name = token::get_ident(it.ident);
|
if it.ident.name == "lintme" {
|
||||||
if name.get() == "lintme" {
|
|
||||||
cx.span_lint(TEST_LINT, it.span, "item is named 'lintme'");
|
cx.span_lint(TEST_LINT, it.span, "item is named 'lintme'");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,11 +10,12 @@ system is up to the task, and gives you powerful ways to reason about
|
|||||||
concurrent code at compile time.
|
concurrent code at compile time.
|
||||||
|
|
||||||
Before we talk about the concurrency features that come with Rust, it's important
|
Before we talk about the concurrency features that come with Rust, it's important
|
||||||
to understand something: Rust is low-level enough that all of this is provided
|
to understand something: Rust is low-level enough that the vast majority of
|
||||||
by the standard library, not by the language. This means that if you don't like
|
this is provided by the standard library, not by the language. This means that
|
||||||
some aspect of the way Rust handles concurrency, you can implement an alternative
|
if you don't like some aspect of the way Rust handles concurrency, you can
|
||||||
way of doing things. [mio](https://github.com/carllerche/mio) is a real-world
|
implement an alternative way of doing things.
|
||||||
example of this principle in action.
|
[mio](https://github.com/carllerche/mio) is a real-world example of this
|
||||||
|
principle in action.
|
||||||
|
|
||||||
## Background: `Send` and `Sync`
|
## Background: `Send` and `Sync`
|
||||||
|
|
||||||
@ -114,7 +115,7 @@ languages. It will not compile:
|
|||||||
use std::thread;
|
use std::thread;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut data = vec![1u32, 2, 3];
|
let mut data = vec![1, 2, 3];
|
||||||
|
|
||||||
for i in 0..3 {
|
for i in 0..3 {
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
@ -152,7 +153,7 @@ use std::thread;
|
|||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut data = Mutex::new(vec![1u32, 2, 3]);
|
let mut data = Mutex::new(vec![1, 2, 3]);
|
||||||
|
|
||||||
for i in 0..3 {
|
for i in 0..3 {
|
||||||
let data = data.lock().unwrap();
|
let data = data.lock().unwrap();
|
||||||
@ -194,7 +195,7 @@ use std::sync::{Arc, Mutex};
|
|||||||
use std::thread;
|
use std::thread;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let data = Arc::new(Mutex::new(vec![1u32, 2, 3]));
|
let data = Arc::new(Mutex::new(vec![1, 2, 3]));
|
||||||
|
|
||||||
for i in 0..3 {
|
for i in 0..3 {
|
||||||
let data = data.clone();
|
let data = data.clone();
|
||||||
@ -216,7 +217,7 @@ thread more closely:
|
|||||||
# use std::sync::{Arc, Mutex};
|
# use std::sync::{Arc, Mutex};
|
||||||
# use std::thread;
|
# use std::thread;
|
||||||
# fn main() {
|
# fn main() {
|
||||||
# let data = Arc::new(Mutex::new(vec![1u32, 2, 3]));
|
# let data = Arc::new(Mutex::new(vec![1, 2, 3]));
|
||||||
# for i in 0..3 {
|
# for i in 0..3 {
|
||||||
# let data = data.clone();
|
# let data = data.clone();
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
@ -254,7 +255,7 @@ use std::thread;
|
|||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let data = Arc::new(Mutex::new(0u32));
|
let data = Arc::new(Mutex::new(0));
|
||||||
|
|
||||||
let (tx, rx) = mpsc::channel();
|
let (tx, rx) = mpsc::channel();
|
||||||
|
|
||||||
@ -292,7 +293,7 @@ fn main() {
|
|||||||
let tx = tx.clone();
|
let tx = tx.clone();
|
||||||
|
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
let answer = 42u32;
|
let answer = 42;
|
||||||
|
|
||||||
tx.send(answer);
|
tx.send(answer);
|
||||||
});
|
});
|
||||||
|
@ -355,6 +355,10 @@ Hello in English: Hello!
|
|||||||
Goodbye in English: Goodbye.
|
Goodbye in English: Goodbye.
|
||||||
```
|
```
|
||||||
|
|
||||||
|
`pub` also applies to `struct`s and their member fields. In keeping with Rust’s
|
||||||
|
tendency toward safety, simply making a `struct` public won't automatically
|
||||||
|
make its members public: you must mark the fields individually with `pub`.
|
||||||
|
|
||||||
Now that our functions are public, we can use them. Great! However, typing out
|
Now that our functions are public, we can use them. Great! However, typing out
|
||||||
`phrases::english::greetings::hello()` is very long and repetitive. Rust has
|
`phrases::english::greetings::hello()` is very long and repetitive. Rust has
|
||||||
another keyword for importing names into the current scope, so that you can
|
another keyword for importing names into the current scope, so that you can
|
||||||
@ -517,9 +521,6 @@ of `foo` relative to where we are. If that’s prefixed with `::`, as in
|
|||||||
`::foo::bar()`, it refers to a different `foo`, an absolute path from your
|
`::foo::bar()`, it refers to a different `foo`, an absolute path from your
|
||||||
crate root.
|
crate root.
|
||||||
|
|
||||||
Also, note that we `pub use`d before we declared our `mod`s. Rust requires that
|
|
||||||
`use` declarations go first.
|
|
||||||
|
|
||||||
This will build and run:
|
This will build and run:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
@ -460,15 +460,15 @@ If you run this program, you’ll see that the philosophers eat out of order!
|
|||||||
We have multi-threading!
|
We have multi-threading!
|
||||||
|
|
||||||
```text
|
```text
|
||||||
Gilles Deleuze is eating.
|
|
||||||
Gilles Deleuze is done eating.
|
|
||||||
Emma Goldman is eating.
|
|
||||||
Emma Goldman is done eating.
|
|
||||||
Michel Foucault is eating.
|
|
||||||
Judith Butler is eating.
|
Judith Butler is eating.
|
||||||
Judith Butler is done eating.
|
Gilles Deleuze is eating.
|
||||||
Karl Marx is eating.
|
Karl Marx is eating.
|
||||||
|
Emma Goldman is eating.
|
||||||
|
Michel Foucault is eating.
|
||||||
|
Judith Butler is done eating.
|
||||||
|
Gilles Deleuze is done eating.
|
||||||
Karl Marx is done eating.
|
Karl Marx is done eating.
|
||||||
|
Emma Goldman is done eating.
|
||||||
Michel Foucault is done eating.
|
Michel Foucault is done eating.
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -33,8 +33,10 @@ pub fn new(value: T) -> Rc<T> {
|
|||||||
```
|
```
|
||||||
|
|
||||||
This code generates documentation that looks [like this][rc-new]. I've left the
|
This code generates documentation that looks [like this][rc-new]. I've left the
|
||||||
implementation out, with a regular comment in its place. That's the first thing
|
implementation out, with a regular comment in its place.
|
||||||
to notice about this annotation: it uses `///`, instead of `//`. The triple slash
|
|
||||||
|
The first thing to notice about this annotation is that it uses
|
||||||
|
`///` instead of `//`. The triple slash
|
||||||
indicates a documentation comment.
|
indicates a documentation comment.
|
||||||
|
|
||||||
Documentation comments are written in Markdown.
|
Documentation comments are written in Markdown.
|
||||||
@ -102,7 +104,7 @@ we could have added more explanation in a new paragraph.
|
|||||||
#### Special sections
|
#### Special sections
|
||||||
|
|
||||||
Next, are special sections. These are indicated with a header, `#`. There
|
Next, are special sections. These are indicated with a header, `#`. There
|
||||||
are three kinds of headers that are commonly used. They aren't special syntax,
|
are four kinds of headers that are commonly used. They aren't special syntax,
|
||||||
just convention, for now.
|
just convention, for now.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
@ -144,7 +146,7 @@ responsible for upholding.
|
|||||||
# fn foo() {}
|
# fn foo() {}
|
||||||
```
|
```
|
||||||
|
|
||||||
Third, `Examples`. Include one or more examples of using your function or
|
Fourth, `Examples`. Include one or more examples of using your function or
|
||||||
method, and your users will love you for it. These examples go inside of
|
method, and your users will love you for it. These examples go inside of
|
||||||
code block annotations, which we'll talk about in a moment, and can have
|
code block annotations, which we'll talk about in a moment, and can have
|
||||||
more than one section:
|
more than one section:
|
||||||
|
@ -50,6 +50,8 @@ is very wrong. Wrong enough that we can't continue with things in the current
|
|||||||
state. Another example is using the `unreachable!()` macro:
|
state. Another example is using the `unreachable!()` macro:
|
||||||
|
|
||||||
```rust,ignore
|
```rust,ignore
|
||||||
|
use Event::NewRelease;
|
||||||
|
|
||||||
enum Event {
|
enum Event {
|
||||||
NewRelease,
|
NewRelease,
|
||||||
}
|
}
|
||||||
@ -71,7 +73,7 @@ fn descriptive_probability(event: Event) -> &'static str {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
std::io::println(descriptive_probability(NewRelease));
|
println!("{}", descriptive_probability(NewRelease));
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -309,8 +309,8 @@ and invokes callbacks from there.
|
|||||||
In these cases access to Rust data structures inside the callbacks is
|
In these cases access to Rust data structures inside the callbacks is
|
||||||
especially unsafe and proper synchronization mechanisms must be used.
|
especially unsafe and proper synchronization mechanisms must be used.
|
||||||
Besides classical synchronization mechanisms like mutexes, one possibility in
|
Besides classical synchronization mechanisms like mutexes, one possibility in
|
||||||
Rust is to use channels (in `std::comm`) to forward data from the C thread
|
Rust is to use channels (in `std::sync::mpsc`) to forward data from the C
|
||||||
that invoked the callback into a Rust thread.
|
thread that invoked the callback into a Rust thread.
|
||||||
|
|
||||||
If an asynchronous callback targets a special object in the Rust address space
|
If an asynchronous callback targets a special object in the Rust address space
|
||||||
it is also absolutely necessary that no more callbacks are performed by the
|
it is also absolutely necessary that no more callbacks are performed by the
|
||||||
@ -340,7 +340,7 @@ libraries:
|
|||||||
Note that frameworks are only available on OSX targets.
|
Note that frameworks are only available on OSX targets.
|
||||||
|
|
||||||
The different `kind` values are meant to differentiate how the native library
|
The different `kind` values are meant to differentiate how the native library
|
||||||
participates in linkage. From a linkage perspective, the rust compiler creates
|
participates in linkage. From a linkage perspective, the Rust compiler creates
|
||||||
two flavors of artifacts: partial (rlib/staticlib) and final (dylib/binary).
|
two flavors of artifacts: partial (rlib/staticlib) and final (dylib/binary).
|
||||||
Native dynamic library and framework dependencies are propagated to the final
|
Native dynamic library and framework dependencies are propagated to the final
|
||||||
artifact boundary, while static library dependencies are not propagated at
|
artifact boundary, while static library dependencies are not propagated at
|
||||||
@ -350,9 +350,9 @@ artifact.
|
|||||||
A few examples of how this model can be used are:
|
A few examples of how this model can be used are:
|
||||||
|
|
||||||
* A native build dependency. Sometimes some C/C++ glue is needed when writing
|
* A native build dependency. Sometimes some C/C++ glue is needed when writing
|
||||||
some rust code, but distribution of the C/C++ code in a library format is just
|
some Rust code, but distribution of the C/C++ code in a library format is just
|
||||||
a burden. In this case, the code will be archived into `libfoo.a` and then the
|
a burden. In this case, the code will be archived into `libfoo.a` and then the
|
||||||
rust crate would declare a dependency via `#[link(name = "foo", kind =
|
Rust crate would declare a dependency via `#[link(name = "foo", kind =
|
||||||
"static")]`.
|
"static")]`.
|
||||||
|
|
||||||
Regardless of the flavor of output for the crate, the native static library
|
Regardless of the flavor of output for the crate, the native static library
|
||||||
@ -361,7 +361,7 @@ A few examples of how this model can be used are:
|
|||||||
|
|
||||||
* A normal dynamic dependency. Common system libraries (like `readline`) are
|
* A normal dynamic dependency. Common system libraries (like `readline`) are
|
||||||
available on a large number of systems, and often a static copy of these
|
available on a large number of systems, and often a static copy of these
|
||||||
libraries cannot be found. When this dependency is included in a rust crate,
|
libraries cannot be found. When this dependency is included in a Rust crate,
|
||||||
partial targets (like rlibs) will not link to the library, but when the rlib
|
partial targets (like rlibs) will not link to the library, but when the rlib
|
||||||
is included in a final target (like a binary), the native library will be
|
is included in a final target (like a binary), the native library will be
|
||||||
linked in.
|
linked in.
|
||||||
@ -533,19 +533,10 @@ attribute turns off Rust's name mangling, so that it is easier to link to.
|
|||||||
|
|
||||||
# FFI and panics
|
# FFI and panics
|
||||||
|
|
||||||
It’s important to be mindful of `panic!`s when working with FFI. This code,
|
It’s important to be mindful of `panic!`s when working with FFI. A `panic!`
|
||||||
when called from C, will `abort`:
|
across an FFI boundary is undefined behavior. If you’re writing code that may
|
||||||
|
panic, you should run it in another thread, so that the panic doesn’t bubble up
|
||||||
```rust
|
to C:
|
||||||
#[no_mangle]
|
|
||||||
pub extern fn oh_no() -> ! {
|
|
||||||
panic!("Oops!");
|
|
||||||
}
|
|
||||||
# fn main() {}
|
|
||||||
```
|
|
||||||
|
|
||||||
If you’re writing code that may panic, you should run it in another thread,
|
|
||||||
so that the panic doesn’t bubble up to C:
|
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
@ -1,85 +0,0 @@
|
|||||||
% for Loops
|
|
||||||
|
|
||||||
The `for` loop is used to loop a particular number of times. Rust’s `for` loops
|
|
||||||
work a bit differently than in other systems languages, however. Rust’s `for`
|
|
||||||
loop doesn’t look like this “C-style” `for` loop:
|
|
||||||
|
|
||||||
```c
|
|
||||||
for (x = 0; x < 10; x++) {
|
|
||||||
printf( "%d\n", x );
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Instead, it looks like this:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
for x in 0..10 {
|
|
||||||
println!("{}", x); // x: i32
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
In slightly more abstract terms,
|
|
||||||
|
|
||||||
```ignore
|
|
||||||
for var in expression {
|
|
||||||
code
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The expression is an [iterator][iterator]. The iterator gives back a series of
|
|
||||||
elements. Each element is one iteration of the loop. That value is then bound
|
|
||||||
to the name `var`, which is valid for the loop body. Once the body is over, the
|
|
||||||
next value is fetched from the iterator, and we loop another time. When there
|
|
||||||
are no more values, the `for` loop is over.
|
|
||||||
|
|
||||||
[iterator]: iterators.html
|
|
||||||
|
|
||||||
In our example, `0..10` is an expression that takes a start and an end position,
|
|
||||||
and gives an iterator over those values. The upper bound is exclusive, though,
|
|
||||||
so our loop will print `0` through `9`, not `10`.
|
|
||||||
|
|
||||||
Rust does not have the “C-style” `for` loop on purpose. Manually controlling
|
|
||||||
each element of the loop is complicated and error prone, even for experienced C
|
|
||||||
developers.
|
|
||||||
|
|
||||||
# Enumerate
|
|
||||||
|
|
||||||
When you need to keep track of how many times you already looped, you can use the `.enumerate()` function.
|
|
||||||
|
|
||||||
## On ranges:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
for (i,j) in (5..10).enumerate() {
|
|
||||||
println!("i = {} and j = {}", i, j);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Outputs:
|
|
||||||
|
|
||||||
```text
|
|
||||||
i = 0 and j = 5
|
|
||||||
i = 1 and j = 6
|
|
||||||
i = 2 and j = 7
|
|
||||||
i = 3 and j = 8
|
|
||||||
i = 4 and j = 9
|
|
||||||
```
|
|
||||||
|
|
||||||
Don't forget to add the parentheses around the range.
|
|
||||||
|
|
||||||
## On iterators:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
# let lines = "hello\nworld".lines();
|
|
||||||
for (linenumber, line) in lines.enumerate() {
|
|
||||||
println!("{}: {}", linenumber, line);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Outputs:
|
|
||||||
|
|
||||||
```text
|
|
||||||
0: Content of line one
|
|
||||||
1: Content of line two
|
|
||||||
2: Content of line tree
|
|
||||||
3: Content of line four
|
|
||||||
```
|
|
@ -3,24 +3,12 @@
|
|||||||
Not every Rustacean has a background in systems programming, nor in computer
|
Not every Rustacean has a background in systems programming, nor in computer
|
||||||
science, so we've added explanations of terms that might be unfamiliar.
|
science, so we've added explanations of terms that might be unfamiliar.
|
||||||
|
|
||||||
### Arity
|
|
||||||
|
|
||||||
Arity refers to the number of arguments a function or operation takes.
|
|
||||||
|
|
||||||
```rust
|
|
||||||
let x = (2, 3);
|
|
||||||
let y = (4, 6);
|
|
||||||
let z = (8, 2, 6);
|
|
||||||
```
|
|
||||||
|
|
||||||
In the example above `x` and `y` have arity 2. `z` has arity 3.
|
|
||||||
|
|
||||||
### Abstract Syntax Tree
|
### Abstract Syntax Tree
|
||||||
|
|
||||||
When a compiler is compiling your program, it does a number of different
|
When a compiler is compiling your program, it does a number of different things.
|
||||||
things. One of the things that it does is turn the text of your program into an
|
One of the things that it does is turn the text of your program into an
|
||||||
‘abstract syntax tree’, or ‘AST’. This tree is a representation of the
|
‘abstract syntax tree’, or ‘AST’. This tree is a representation of the structure
|
||||||
structure of your program. For example, `2 + 3` can be turned into a tree:
|
of your program. For example, `2 + 3` can be turned into a tree:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
+
|
+
|
||||||
@ -37,3 +25,41 @@ And `2 + (3 * 4)` would look like this:
|
|||||||
/ \
|
/ \
|
||||||
3 4
|
3 4
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Arity
|
||||||
|
|
||||||
|
Arity refers to the number of arguments a function or operation takes.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let x = (2, 3);
|
||||||
|
let y = (4, 6);
|
||||||
|
let z = (8, 2, 6);
|
||||||
|
```
|
||||||
|
|
||||||
|
In the example above `x` and `y` have arity 2. `z` has arity 3.
|
||||||
|
|
||||||
|
### Expression
|
||||||
|
|
||||||
|
In computer programming, an expression is a combination of values, constants,
|
||||||
|
variables, operators and functions that evaluate to a single value. For example,
|
||||||
|
`2 + (3 * 4)` is an expression that returns the value 14. It is worth noting
|
||||||
|
that expressions can have side-effects. For example, a function included in an
|
||||||
|
expression might perform actions other than simply returning a value.
|
||||||
|
|
||||||
|
### Expression-Oriented Language
|
||||||
|
|
||||||
|
In early programming languages, [expressions][expression] and
|
||||||
|
[statements][statement] were two separate syntactic categories: expressions had
|
||||||
|
a value and statements did things. However, later languages blurred this
|
||||||
|
distinction, allowing expressions to do things and statements to have a value.
|
||||||
|
In an expression-oriented language, (nearly) every statement is an expression
|
||||||
|
and therefore returns a value. Consequently, these expression statements can
|
||||||
|
themselves form part of larger expressions.
|
||||||
|
|
||||||
|
[expression]: glossary.html#expression
|
||||||
|
[statement]: glossary.html#statement
|
||||||
|
|
||||||
|
### Statement
|
||||||
|
|
||||||
|
In computer programming, a statement is the smallest standalone element of a
|
||||||
|
programming language that commands a computer to perform an action.
|
||||||
|
@ -98,8 +98,8 @@ use std::io;
|
|||||||
|
|
||||||
We’ll need to take user input, and then print the result as output. As such, we
|
We’ll need to take user input, and then print the result as output. As such, we
|
||||||
need the `io` library from the standard library. Rust only imports a few things
|
need the `io` library from the standard library. Rust only imports a few things
|
||||||
into every program, [the ‘prelude’][prelude]. If it’s not in the prelude,
|
by default into every program, [the ‘prelude’][prelude]. If it’s not in the
|
||||||
you’ll have to `use` it directly.
|
prelude, you’ll have to `use` it directly.
|
||||||
|
|
||||||
[prelude]: ../std/prelude/index.html
|
[prelude]: ../std/prelude/index.html
|
||||||
|
|
||||||
@ -360,10 +360,12 @@ rand="0.3.0"
|
|||||||
The `[dependencies]` section of `Cargo.toml` is like the `[package]` section:
|
The `[dependencies]` section of `Cargo.toml` is like the `[package]` section:
|
||||||
everything that follows it is part of it, until the next section starts.
|
everything that follows it is part of it, until the next section starts.
|
||||||
Cargo uses the dependencies section to know what dependencies on external
|
Cargo uses the dependencies section to know what dependencies on external
|
||||||
crates you have, and what versions you require. In this case, we’ve used version `0.3.0`.
|
crates you have, and what versions you require. In this case, we’ve specified version `0.3.0`,
|
||||||
|
which Cargo understands to be any release that’s compatible with this specific version.
|
||||||
Cargo understands [Semantic Versioning][semver], which is a standard for writing version
|
Cargo understands [Semantic Versioning][semver], which is a standard for writing version
|
||||||
numbers. If we wanted to use the latest version we could use `*` or we could use a range
|
numbers. If we wanted to use only `0.3.0` exactly, we could use `=0.3.0`. If we
|
||||||
of versions. [Cargo’s documentation][cargodoc] contains more details.
|
wanted to use the latest version we could use `*`; We could use a range of
|
||||||
|
versions. [Cargo’s documentation][cargodoc] contains more details.
|
||||||
|
|
||||||
[semver]: http://semver.org
|
[semver]: http://semver.org
|
||||||
[cargodoc]: http://doc.crates.io/crates-io.html
|
[cargodoc]: http://doc.crates.io/crates-io.html
|
||||||
@ -497,7 +499,7 @@ generator, which is local to the particular [thread][concurrency] of execution
|
|||||||
we’re in. Because we `use rand::Rng`’d above, it has a `gen_range()` method
|
we’re in. Because we `use rand::Rng`’d above, it has a `gen_range()` method
|
||||||
available. This method takes two arguments, and generates a number between
|
available. This method takes two arguments, and generates a number between
|
||||||
them. It’s inclusive on the lower bound, but exclusive on the upper bound,
|
them. It’s inclusive on the lower bound, but exclusive on the upper bound,
|
||||||
so we need `1` and `101` to get a number between one and a hundred.
|
so we need `1` and `101` to get a number ranging from one to a hundred.
|
||||||
|
|
||||||
[concurrency]: concurrency.html
|
[concurrency]: concurrency.html
|
||||||
|
|
||||||
|
@ -8,13 +8,13 @@ so it is assumed that Rust projects will use Cargo from the beginning.
|
|||||||
[cratesio]: http://doc.crates.io
|
[cratesio]: http://doc.crates.io
|
||||||
|
|
||||||
Cargo manages three things: building your code, downloading the dependencies
|
Cargo manages three things: building your code, downloading the dependencies
|
||||||
your code needs, and building those dependencies. At first, your
|
your code needs, and building those dependencies. At first, your program doesn’t
|
||||||
program doesn’t have any dependencies, so we’ll only be using the first part of
|
have any dependencies, so we’ll only be using the first part of its
|
||||||
its functionality. Eventually, we’ll add more. Since we started off by using
|
functionality. Eventually, we’ll add more. Since we started off by using Cargo,
|
||||||
Cargo, it'll be easy to add later.
|
it'll be easy to add later.
|
||||||
|
|
||||||
If you installed Rust via the official installers you will also have Cargo. If
|
If we installed Rust via the official installers we will also have Cargo. If we
|
||||||
you installed Rust some other way, you may want to [check the Cargo
|
installed Rust some other way, we may want to [check the Cargo
|
||||||
README][cargoreadme] for specific instructions about installing it.
|
README][cargoreadme] for specific instructions about installing it.
|
||||||
|
|
||||||
[cargoreadme]: https://github.com/rust-lang/cargo#installing-cargo-from-nightlies
|
[cargoreadme]: https://github.com/rust-lang/cargo#installing-cargo-from-nightlies
|
||||||
@ -23,20 +23,21 @@ README][cargoreadme] for specific instructions about installing it.
|
|||||||
|
|
||||||
Let’s convert Hello World to Cargo.
|
Let’s convert Hello World to Cargo.
|
||||||
|
|
||||||
To Cargo-ify our project, we need to do two things: Make a `Cargo.toml`
|
To Cargo-ify our project, we need to do three things: Make a `Cargo.toml`
|
||||||
configuration file, and put our source file in the right place. Let's
|
configuration file, put our source file in the right place, and get rid of the
|
||||||
do that part first:
|
old executable (`main.exe` on Windows, `main` everywhere else). Let's do that part first:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ mkdir src
|
$ mkdir src
|
||||||
$ mv main.rs src/main.rs
|
$ mv main.rs src/main.rs
|
||||||
|
$ rm main # or main.exe on Windows
|
||||||
```
|
```
|
||||||
|
|
||||||
Note that since we're creating an executable, we used `main.rs`. If we
|
Note that since we're creating an executable, we retain `main.rs` as the source
|
||||||
want to make a library instead, we should use `lib.rs`. This convention is required
|
filename. If we want to make a library instead, we should use `lib.rs`. This
|
||||||
for Cargo to successfully compile our projects, but it can be overridden if we wish.
|
convention is used by Cargo to successfully compile our projects, but it can be
|
||||||
Custom file locations for the entry point can be specified
|
overridden if we wish. Custom file locations for the entry point can be
|
||||||
with a [`[lib]` or `[[bin]]`][crates-custom] key in the TOML file.
|
specified with a [`[lib]` or `[[bin]]`][crates-custom] key in the TOML file.
|
||||||
|
|
||||||
[crates-custom]: http://doc.crates.io/manifest.html#configuring-a-target
|
[crates-custom]: http://doc.crates.io/manifest.html#configuring-a-target
|
||||||
|
|
||||||
@ -73,7 +74,8 @@ extra goodies. According to the TOML docs,
|
|||||||
|
|
||||||
[toml]: https://github.com/toml-lang/toml
|
[toml]: https://github.com/toml-lang/toml
|
||||||
|
|
||||||
Once you have this file in place, we should be ready to build! To do so, run:
|
Once we have this file in place in our project's root directory, we should be
|
||||||
|
ready to build! To do so, run:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ cargo build
|
$ cargo build
|
||||||
|
@ -111,10 +111,13 @@ string to the screen. Easy enough!
|
|||||||
|
|
||||||
[allocation]: the-stack-and-the-heap.html
|
[allocation]: the-stack-and-the-heap.html
|
||||||
|
|
||||||
Finally, the line ends with a semicolon (`;`). Rust is an ‘expression oriented’
|
Finally, the line ends with a semicolon (`;`). Rust is an [‘expression oriented’
|
||||||
language, which means that most things are expressions, rather than statements.
|
language][expression-oriented language], which means that most things are
|
||||||
The `;` is used to indicate that this expression is over, and the next one is
|
expressions, rather than statements. The `;` is used to indicate that this
|
||||||
ready to begin. Most lines of Rust code end with a `;`.
|
expression is over, and the next one is ready to begin. Most lines of Rust code
|
||||||
|
end with a `;`.
|
||||||
|
|
||||||
|
[expression-oriented language]: glossary.html#expression-oriented-language
|
||||||
|
|
||||||
Finally, actually compiling and running our program. We can compile with our
|
Finally, actually compiling and running our program. We can compile with our
|
||||||
compiler, `rustc`, by passing it the name of our source file:
|
compiler, `rustc`, by passing it the name of our source file:
|
||||||
|
@ -166,3 +166,12 @@ unsafe {
|
|||||||
println!("eax is currently {}", result);
|
println!("eax is currently {}", result);
|
||||||
# }
|
# }
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## More Information
|
||||||
|
|
||||||
|
The current implementation of the `asm!` macro is a direct binding to [LLVM's
|
||||||
|
inline assembler expressions][llvm-docs], so be sure to check out [their
|
||||||
|
documentation as well][llvm-docs] for more information about clobbers,
|
||||||
|
constraints, etc.
|
||||||
|
|
||||||
|
[llvm-docs]: http://llvm.org/docs/LangRef.html#inline-assembler-expressions
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user