A Guide To Porting C And C++ Code Rust
User Manual: Pdf
Open the PDF directly: View PDF
.
Page Count: 254 [warning: Documents this large are best viewed by clicking the View PDF Link!]
- Introduction
- Licence
- Foreword
- Credits
- Notation used through this book
- Setting Up Rust
- C and C++ Background
- Rust Background
- Let's Start Simple
- Compiling and Linking in More Detail
- Source Layout and Other General Points
- Namespacing With Modules
- Porting Code
- Features of Rust compared with C++
- Types
- Strings
- Variables
- Literals
- Collections
- Structs
- Comments
- Lifetimes, References and Borrowing
- Expressions
- Conditions
- Switch / Match
- Casting
- Enumerations
- Loops
- Functions
- Polymorphism
- Error Handling
- Lambda Expressions / Closures
- Templates / Generics
- Attributes
- Multi-threading
- Lint
- Macros
- Memory Allocation
- Foreign Function Interface
- Porting from C/C++ to Rust
- Copy Constructor / Assignment Operators
- Missing Braces in Conditionals
- Assignment in Conditionals
- Class Member Initialisation
- Headers and Sources
- Forward Declarations
- Namespace Collisions
- Macros
- Type Mismatching
- Explicit / Implicit Class Constructors
- Poor Lifetime Enforcement
- Memory Allocation
- Null Pointers
- Virtual Destructors
- Exception Handling / Safety
- Templates vs Generics
- Multiple Inheritance
- Linker Errors
- Debugging Rust
- Memory Management
- Rust's std:: library
- Rust Cookbook

1.1
1.2
1.3
1.4
1.5
1.6
1.7
1.8
1.9
1.10
1.11
1.12
1.13
1.14
1.14.1
1.14.2
1.14.3
1.14.4
1.14.5
1.14.6
1.14.7
1.14.8
1.14.9
1.14.10
1.14.11
1.14.12
1.14.13
1.14.14
1.14.15
1.14.16
TableofContents
Introduction
Licence
Foreword
Credits
Notationusedthroughthisbook
SettingUpRust
CandC++Background
RustBackground
Let'sStartSimple
CompilingandLinkinginMoreDetail
SourceLayoutandOtherGeneralPoints
NamespacingWithModules
PortingCode
FeaturesofRustcomparedwithC++
Types
Strings
Variables
Literals
Collections
Structs
Comments
Lifetimes,ReferencesandBorrowing
Expressions
Conditions
Switch/Match
Casting
Enumerations
Loops
Functions
Polymorphism
1

1.14.17
1.14.18
1.14.19
1.14.20
1.14.21
1.14.22
1.14.23
1.14.24
1.14.25
1.15
1.15.1
1.15.2
1.15.3
1.15.4
1.15.5
1.15.6
1.15.7
1.15.8
1.15.9
1.15.10
1.15.11
1.15.12
1.15.13
1.15.14
1.15.15
1.15.16
1.15.17
1.15.18
1.16
1.17
1.18
1.19
ErrorHandling
LambdaExpressions/Closures
Templates/Generics
Attributes
Multi-threading
Lint
Macros
MemoryAllocation
ForeignFunctionInterface
PortingfromC/C++toRust
CopyConstructor/AssignmentOperators
MissingBracesinConditionals
AssignmentinConditionals
ClassMemberInitialisation
HeadersandSources
ForwardDeclarations
NamespaceCollisions
Macros
TypeMismatching
Explicit/ImplicitClassConstructors
PoorLifetimeEnforcement
MemoryAllocation
NullPointers
VirtualDestructors
ExceptionHandling/Safety
TemplatesvsGenerics
MultipleInheritance
LinkerErrors
DebuggingRust
MemoryManagement
Rust'sstd::library
RustCookbook
2

3

AGuidetoPortingC/C++toRust
ThisbookisforpeoplefamiliarwithCorC++whoarethinkingofusingRust.
BeforewegointowhatRustisorwhyitmightbepreferabletoC/C++insomecases,let's
thinkofsoftwarethatismissioncriticalandmustnotorshouldnotfail.
Operatingsystemservicesanddaemons
Internetofthingsdevices
Industrialcontrolsoftware
Medicaldevices-MRI,ultrasound,X-ray,ventilatorsetc.
Highavailabilityservers/databases/cloudstorageetc.
Avionics,telemetry,rocketry,dronesetc.
Allthiscodemustrunasefficientlyandreliablyaspossible.Itmustrunondevicesfordays,
weeks,monthsorpreferablyyearswithoutfailure.Itcannotsufferintermittentfreezes,
erraticperformance,memoryleaks,crashesorotherissueswithoutimpactingonits
purpose.
NormallysuchsoftwarewouldbewritteninCorC++,butconsidertheseeveryday
programmingissuesthatcanafflicttheselanguages:
Danglingpointers.Aprogramcallsaninvalidpointercausingacrash.
Bufferoverruns/underruns.Codewritesbeyondanallocatedbuffercausingmemory
corruptionorapageexception.
Memoryleaks.Codethatallocatesmemoryorresourceswithoutcallingthe
correspondingfreeaction.C++providesclassessuchassmartpointersandtechniques
likeRAIItomitigatetheseissuesbutstilloccur.
Dataraces.Multiplethreadswritetodataatthesametimecausingcorruptionorother
destabilizingbehavior.
Ruststopsthesebadthingshappeningbydesign.Anditdoessowithoutimpactingon
runtimeperformancebecauseallofthesethingsarecheckedatcompiletime:
Objectlifetimesaretrackedautomaticallytopreventmemoryleaksanddangling
pointers.
Thelengthofarraysandcollectionsisenforced.
Dataraceconditionsarepreventedbystrictenforcementofmutex/guardsandobject
ownership.
Codethatpassesthecompiler'schecksistransformedintomachinecodewithsimilar
performanceandspeedastheequivalentCorC++.
Introduction
4

Thisisa"zero-cost"approach.Thecompilerenforcestherulessothatthereiszeroruntime
costovertheequivalentandcorrectlywrittenprograminCorC++.Safetydoesnot
compromiseperformance.
InadditionRustplayswellC.YoumayinvokeCfromRustorinvokeRustfromCusing
foreignfunctioninterfaces.Youcanchoosetorewriteacriticalsectionofyourcodebase
leavetheremainderalone.
Forexample,theFirefoxbrowserusesRusttoanalysevideostreamdata-headersand
suchlikewherecorruptormaliciouscodecoulddestabilizethebrowserorevenbe
exploitable.
WhyRust?
Let'sstartbysayingifwhatyouhaveworksandisreliable,thentheanswertothequestion
is"there'snoreason"youshouldconsiderporting.
Howeverifyouhavecodethatdoesn'tworkorisn'treliable,orhasn'tbeenwrittenyetoris
dueamajorrewritethenperhapsyouhaveansweredyourownquestion.
YoucouldwritethecodeorfixesinC/C++inwhichcaseyouhavetodealwithalltheunsafe
issuesthatthelanguagedoesnotprotectyoufrom.Oryoumightconsiderthatchoosinga
safe-by-designlanguageisagoodwaytoprotectyoufromsufferingbugsinthefieldwhen
thecodeissupposedtobereadyforproduction.
Rustisnotamagicwand
Despitethethingsthelanguagecanprotectyouagainst,itcannotprotectyouagainstthe
following:
Generalraceconditionssuchasdeadlocksbetweenthreads
Unboundedgrowth,e.g.aloopthatpushesvaluesontoavectoruntilmemoryis
exhausted.
Applicationlogicerrors,i.e.errorsthathavenothingtodowiththeunderlyinglanguage,
e.g.missingoutthelinethatshouldsay"ifdoor_open{sound_alarm();}"
Explicitunsafesectionsdoingunsafeanderroneousthings
ErrorsinLLVMorsomethingoutsideofRust'scontrol.
Introduction
5

Licence
Thebookiswrittenundertheseterms:
ThisworkislicensedunderaCreativeCommonsAttribution-NonCommercial-ShareAlike4.0
InternationalLicense.
Refertothelinkfortheexactlegalterms.Butinessenceyoumayshareandmodifythis
bookprovidingyoudonotsellorderiveprofitfromdoingso.
Licence
6

Foreword
BEGINDRAFTBOOKDISCLAIMER
Someofthesampleswillnotcompileormaynothavebeensyntaxchecked
CandRustcodesnippetsarenotdistinguishedverywellyet(styling)
Someofthetextmakesuncitedassertionsoffact
SomeofthetextismarkedTODO
Someofthetopicsthatshouldbecoveredarebrushedover,givenundueweightor
omittedentirely
Someofthetextprobablymakesnosenseorrepeatsitself
WITHALLTHATINMIND,readon!
ENDDRAFTBOOKDISCLAIMER
Thinkofallthesoftwarethatneedstobereliableinthisworld.Softwarethatcanillafford
downtimeorcrashes.Softwarethatismissioncriticalandmustnotorshouldnotfail.
Operatingsystemservicesanddaemons
Internetofthingsdevices
Industrialcontrolsoftware
Medicaldevices,imageryetc.
Highavailabilityservers/databases/cloudstorageetc.
Avionics,telemetry,rocketry,dronesetc.
Allthiscodethathastorunasefficientlyandreliablyaspossiblewiththeminimaloferrors.
Italsohastobepredictablewithoutsuddenfreezesormystery-memorybehaviordueto
garbagecollection.
CandC++hasthespeedanglecoveredbutishardtomakereliable.AlanguagelikeJava
wouldhavethereliabilityanglecoveredbutishardtomakeperformant.
WhatwewantissomethingwhichrunsasfastasCorC++buthasthereliabilitythatgoes
withit.AndthatiswhatRustisabout.Itcompilesintobinaryexecutablesorlibrariesjustlike
CorC++andcanevenbeusedtoproducedynamiclibrariesthatcanbeconsumedbyother
codebodies.
Foreword
7

Alloftheinformationfoundinthisdocumentcanbegleanedfromelsewherebutittendsto
bescatteredacrossdocumentsandsitesthatarefocusedontopicsnotandinmanycases
I'vesoughtinspirationandknowledgefromTODO
1. OnlinedocumentationforRust
2. TheRustonomicon-describessomeoftheesotericreasoningandinternalsbehindthe
languageandhowtoperformunsafeprogramming.Unsafeprogrammingisnotthe
defaultbutisnecessaryforinteractingwithexternalcode.
3. TODO-Designpatternsrepo
4. TODO-EffectiveC++,Meyers
5. TODO–variousstackoverflowquestionsandanswers
Credits
8

Notationusedthroughthisbook
CodesamplesaregiventhroughoutthisbookareforC,C++,Rustandgeneralconfiguration
/consoleoutput.
Inordertodistinguisheachkindtheyarestyledasfollows:
C/C++samplesaregiveninthisstyle:
//C/C++
while(x<y){
cout<<"xislessthany"<<endl;
++x;
}
Rustsamplesaregiveninthisstyle:
//Rust
ifx==20{
println!("Warning!");
}
Standardconsoleoutputorscriptisgiventhisstyle:
cdmyproject/
cargobuild
Mostofthecodesamplesareabbreviatedinsomefashion.e.g.theyassumethecodeis
runningfromwithinamain()functionortheyomitnoisesuchas#includes,namespace
definitionsandsoforth.
Notationusedthroughthisbook
9

SettingupRust
ThissectionwilltalkyouthroughsettingupRustforthefirsttimeandalsohowtokeepitup
todate.
Gettingstartedisincrediblyeasybutsomedetailsvaryuponyourtargetplatform.Rustruns
onWindows,LinuxandMacOS.Inadditionyoumightwishtocross-compilecodeforthe
consumptionofanotherplatform.
UseRustup
Theeasiestwaytogetstartedistodownloadandrun rustup-initwhichyoucandoby
visitingtheRustupsite.
TheinstructionsdifferforWindowsandUnix-likesystems:
OnWindows,rustup-initisanexeinstaller.
OnUnix/OSX/Linuxrust-initisashellscript.
Eitherway,whenyoufollowtheinstructionstheinstallerwilldownloadandinstallputrustc,
cargo,andrustupinyourbindirectorywhichis ~/.cargo/binonUnixand
%USERPROFILE%.cargo.\binonWindows.
Itwillalsosetyour PATHenvironmentvariablesothatyoucanopenaterminalandtype
rustc,cargo,rustupetc.
Once rustupisinstalledyoucanalsousethetoolformaintenance:
InstalladditionalRusttoolchains(e.g.ifyouarecross-compilingorsupportingmultiple
targetsyoumayhavemorethanonetoolchain)
Changethedefaulttoolchainthatisinvokedwhenyoutype rustcor cargo.Rustup
willcreatesymboliclinks/scriptsthatinvoketheappropriatetoolchain
UpdatethetoolchainwhenanewversionofRustisreleased
Fetchsourceanddocumentation
Unix/Linux
Theprocessforrunning rustup-init.shisasfollows:
1. Openaterminal/console
2. Type"curlhttps://sh.rustup.rs-sSf|sh"
SettingUpRust
10

3. Thiswilldownloadandexecuteascriptwhichwillexamineyourenvironment,
recommendthetoolchaintodownload,andoffertomodifyyour PATHenvironment
variable.
4. Choosetheoption1toproceed.Orcustomizeifyouwanttomodifysomething
5. Waitfordownloadtocomplete
6. You'redone.
Ifyoudon'thavecurl,thenyoumustinstallitfirsttoproceed,orsavetheshellscriptfroma
browsertodiskandexecutethat.
Toinstall curlinLinuxyouwouldinvokeacommandlikethistoinstallit.
Debian/Ubuntu- sudoaptgetcurl
Fedora/Redhat- sudodnfinstallcurl
Windows
1. Downloadrustup-init.exefromrustup.rs.
2. Doubleclickontherust-init.exeandaconsolewillopen
3. Choosetheoption1toproceed.Orcustomizeifyouwanttomodifysomething
4. Waitfordownloadtocomplete
5. You'redone.
Ifyouprefernottogowiththedefaults,herearesomechoicesyoushoulddecideupon:
1. 32/64bitversion.MostWindowsinstallationsaregoingtobe64-bitsthesedaysbutyou
mayhaveareasontochoose32-bit.
2. GNUorMSVCABI.Thisdependsonwhattoolchainandruntimesyouwishtobe
compatiblewith.
Thesecondchoiceconcernstheapplicationbinaryinterface(ABI)youwantRusttobe
compatiblewith.
Ifyoudon'tcareaboutlinkingtoanythingthenchoosetheGNUABI.Alsochooseitif
youhaveDLLsproducedbyMingW/MSYS.TheadvantageofthisABIisthatitismore
mature.
IfyouhaveVisualStudioinstalledorintendtouseRustagainstDLLscreatedwith
VisualStudio,that'stheABIyouneed.Oneadvantageofthisoptionisthatyoucan
debugRustinsideofVisualStudio-thecompilerwillproduce.pdbfilesthatallowyouto
stepdebugRust.
KeepingRustuptodate
SettingUpRust
11

NewversionsofRustappearinasemi-frequentbasis.Ifyouwanttoupdateyour
environmenttothelatestversion,itisassimpleasthis:
rustupupdate
Sometimesrustupwillgetanupdateofitsowninwhichcaseyoutype:
rustupselfupdate
AddingRustsource
Rustupinstallsarusttoolchainbutifyou'rewritingcodeordebuggingyouprobablyshould
alsogettheRustsourcecodesoyoucanstepintoitorlookattheimplementation:
rustupcomponentaddrust-src
Manualinstallation
IfyouprefermanualinstallationofRustthentherearepackagesandinstructionsonthe
Rustsite.
JustbeawarethatRusthasafairlyrapidreleasecyclesoyouprobablyonlywanttodothis
ifyouhaveareasontochooseaspecificversionofRustandstickwithit.
Otherwiseyoumayfindyourselfuninstallingandreinstallinganewversion6weekslaterall
overagain.
Settingupadebugger
Unix/Linux
DebuggingRustislittledifferentfromdebuggingCorC++.
Youmustinstallgdbforyourplatformandthenyoumayinvokeitfromaconsoleoryour
favouritefront-endtodebugRustcode.
OnLinuxsystemsyouwouldnormallyinstallgdbfromapackagewithoneofthese
commands:
SettingUpRust
12

sudoapt-getinstallgdb
#or
sudodnfinstallgdb
YoumayalsoprefertouselldbwhichisacompanionprojecttoLLVM(thebackendcompiler
usedbyRust).Refertothelldbwebsiteforinformationonusingit.
Rustcomeswithafewscriptsthatwrapgdbandlldbtoprovidepretty-printingtoassistwith
debugging.Whendebugging,youcaninvoke rust-gdbor rust-lldbtousethem.
Windows
IfyouhavechosenRustwiththeMSVCABIthenyoucandebugthroughVisualStudiowith
somelimitations.Whenyoucreateadebugbuildofyourcode,thecompilewillalsocreatea
.pdbfiletogowithit.YoumayopenyourexecutableinVisualStudioandstepdebugit,
inspectvariablesandsoon.
GDB
GDBonWindowsisavailablethroughMSYS/MingWdistributions.
ForexampledownloadsoftheTDM-GCCdistributionofMSYScanbefoundhere.Atthe
timeofwritingthis,thereisastandalonegdb-7.9.1-tdm64-2.zipcontainingtheChoosethe
32or64-bitversionaccordingtoyourRustenvironment.
Extractthezipfiletoadirectory,e.g. C:\tools\gdb-7.9.1-tdm64-2andaddavaluetoyour
PATHenvironmentvariable:
setPATH=%PATH%;C:\tools\gdb-7.9.1-tdm64-2\bin\
Youcaninvoke gdbfromthecommandlinebutmorenormallyyou'dpreferafrontend.
Atthetimeofwriting,perhapsthebestoptionisVisualStudioCodewhichhaspluginsfor
debuggingwithGDBandforRustdevelopment.Soyoucaneditanddebugfromthesame
IDE.
Prettyprinter
RustsuppliesaprettyprinterforvariableinspectionthatyoucanaddtotheGDB.Thepretty
printerisascriptwritteninPythonthatGDBwillinvoketodisplayvariables.
FirstensureyouhavePython2.7installedinyourpath.
SettingUpRust
13

ThescriptisbundledwiththeRustsourcecodesoyouneedtohaveinstalledthatfirst.
Ifyouinstalleditwith rustupthenitcanbefoundinyour %USERPROFILE%\.rustupdirectory:
e.g.
c:\users\MyName\.rustup\toolchains\stable-x86_64-pc-windows-
gnu\lib\rustlib\src\rust\src\etc
OtherwiseitcanbefoundwhereveryouunzippedyourRustsourcecodeunder
src\rust\src\etc.
Notethefullyqualifiedpathitsunderandedit C:\tools\gdb-7.9.1-tdm64-2\bin\gdbinitto
insertthepathusingforwardslashes.
python
print"----LoadingRustpretty-printers----"
sys.path.insert(0,"C:/users/MyName/.rustup\toolchains/stable-
x86_64-pc-windows-gnu/lib/rustlib/src/rust/src/etc")
importgdb_rust_pretty_printing
gdb_rust_pretty_printing.register_printers(gdb)
end
SettingupanIDE
RustisstillbehindsomeotherlanguageswhenitcomestoIDEintegrationbutthereare
alreadypluginsthatprovidemuchofthefunctionalityyouneed.
PopularIDEssuchasEclipse,IntelliJ,VisualStudioallhavepluginsthatworktovarying
degreesofintegrationwithRust.
VisualStudioCode(nottobeconfusedwithVisualStudio)isacross-platform
programmingeditorandhasalotofplugins.ItcanbesetupintoacompleteRust
developmentenvironmentbyfollowingthistutorial.
RustpluginforIntelliJIDEAisunderactivedevelopment.Thispluginhasalotof
tractionandisturningaroundnewversionsonanearlyweeklybasis.Offerssyntax
highlighting,autocomplete(viabuilt-inparser),cargobuiltsandeventuallyother
functionality.IntelliJisacommercialproductbutitcomesinacommunityeditionwhich
issufficientfordevelopment.
SettingUpRust
14

VisualRustpluginforMicrosoftStudio.Offerssyntaxhighlighting,autocompletion,
interactivedebugging.
RustDTforEclipseisalsounderactivedevelopment.Itaddssyntaxhighlighting,
autocomplete(viaracer),cargobuildsandrustfmtfunctionalitytoEclipse.
Atomisapopulareditorwithheapsofplugins.ThesepluginsareveryusefulforRust:
language-rustprovidesbasicsyntaxhighlighting
racerforautocompletionfunctionality
atom-beautifyinvokesrustfmttomakecodelookpretty.
build-cargoinvokescargoforyoushowingerrorsandwarningsinline.
ForothereditorsandIDEsrefertotheRustandIDEspageontheRustwebsite.
Racer/Rustfmt
SomeofthepluginsabovemakeuseofRacerandRustfmt.
Racerisusedbysomepluginstoprovideautocompletionfunctionality.
RustfmtisasourcecodeformattingtoolthatmakessureyourRustsourcecodeisprettyto
lookat,addingspacing,indentationandsoon.
Youcangetbothjustbytypingthesecommandsandwaitingforthetoolstodownloadand
buildthemselves-they'rewritteninRustandbuiltthroughcargo.
cargoinstallracer
cargoinstallrustfmt
SettingUpRust
15

CandC++Background
ThissectiontalksaboutCandC++.Itdescribesitshistory,standardsandprovidesa
backgroundastohowitendedupwhereitistoday.
HistoryofC
EarlyDays
ThecreationofCiscloselyassociatedwiththeearlydaysofUnix.BellLabsdevelopedUnix
outofanearlierprojectcalledMultics.ThefirstversionofUnixranonPDP-7microcomputer
andfundingwasgiventomoveittoPDP-11.DennisRitchiewasakeymemberonthis
projectandsetaboutcreatingalanguagethatcouldhelphimdevelopUnixwhileminimizing
theamountofassemblylanguagehehadtowrite.Mostofthecodeuptothatpointwas
expressedinassemblylanguagewhichwaserrorproneandobviouslynonportable.
RitchiedevelopedCsothathecouldwritecodeintermsofvariables,expressions,loops,
functionsetc.anduseacompilertotranslateCcodeintomachinecode.Thegenerated
coderanalmostasfastashandwrittenassemblyandwasmoreportablesinceonlythe
compilerhadtobechangedinordertosupportanewarchitecture.Citselfwasinfluenced
byB(hencewhyitwascalledC),whichitselfwasinfluencedbyBCPL.
Defactostandardandemergingpopularity
In1978CwasformalisedintoadefactostandardcalledK&RC,namedafterBrian
Kernighan&DennisRitchewhopublishedthestandardasabook.
OvertimetheuseofCbecamemorewidespreadandcompilerssuchasTurboC,LatticeC,
MicrosoftCpopularizedConotheroperatingsystemsincludingpersonalcomputers.
InternationalStandards
ClaterbecameanANSIstandard,C89.AfurtherstandardfollowedwithC99andCisstill
underreviewanddevelopment.
SomefunctionalitythatwasintroducedinC++hasalsofounditswaybackintoCstandards.
Forexample,the//stylesingle-linecommentandvariabledeclarationrulesinblocks.
CandC++Background
16

HistoryofC++
C++firstappearedin1983asCwithclasses.ItwasinventedbyBjarneStroustropasaway
toimbueCwithSimula-likefeatures.Simulaisalanguagethatallowedconceptssuchas
objects,classesandinheritancetobeexpressedincodeandasitsnamesuggestswas
createdforrunningsimulations.Howeveritwasconsideredtooslowforsystems
programmingandsosomethingthatcombinedspeedofCwithobjectorientedconceptswas
highlydesirable.
C++addedtheseconceptsasextensionstotheClanguageandusedaprecompilercalled
cfronttotransformtheC++extensionsintoCcodethatcouldthenbecompiledinto
machinecode.SoaC++programcouldhavethehighlevelobjectorientedconceptsbut
withouttheoverheadthatcamewithSimula.
C++becamepopularinitsownrightandoutgrewthelimitationsofcfrontpreprocessorto
becomesupportedbycompilersinitsownright.ThustoolchainssuchasMicrosoftVisual
C++,GCC,Clangetc.supportbothlanguages.Sometoolchainshavealsobeengivento
favouringC++overC,forexampleMicrosoft'scompilerhasbeenveryslowtoimplement
C99.
Objectorientedprogramminghasmostlybeenusedinhigherlevelsoftware-applications,
games,simulationsandmathematicalwork.
C++hasalsobecomeformalisedstandardswithC++98,C++03,C++11andsoon.
ModernC++
C++11onwardsisadistinctlydifferentbeastfromearlieriterationsandstrivestoadd
functionalitythatifusedcorrectlycaneliminatealotofissuesthatwillbediscussedlateron:
Scopedandsharedpointers
autokeyword
movesemantics(i.e.movingdataownershipofdatafromonevariabletoanother)
rvaluereferences
perfectforwarding
nullptrexplicittype
HoweveritisworthnotingthatsincemanyofthesethingsarelateadditionstoC++.Things
likemovesemanticsmustbeexplicitlyusedandhaveimplicationsthatarenotanissuefor
Rustwheretheyhavebeenpartofthelanguagesinceearlyon.
TherelationshipbetweenCandC++
CandC++Background
17

WhileC++grewoutofCandhasdevelopedalongsideit,itisnottruetosayC++isa
supersetofC.Ratheritismostlyasuperset.Therearedifferencessuchaskeywordsand
headersthatCrecognizesthatC++doesnot.
C++hasfunctionoverloadingandclassesandusesnamemanglingtodisambiguate
overloadedfunctions.ButinpracticeitispossibletowriteCasasubsetofC++andcompile
thetwointothesameexecutable.Mostreal-worldCcodecouldbecalledC++without
classes.
CandC++areevenusuallyhandledbythesametoolchain.Mostcompilerswouldconsistof
afronthalfthatparsesthelanguageintoanintermediateformandabackhalfwhichturns
theintermediateformintooptimizedmachinecode.Finallythelinkerwouldjoinallthebinary
objectstogethertoformanexecutable.CandC++wouldsharemostofthiscodepath.
C++tendstobemorepopularwithapplicationslevelprogramming.PartofthereasonC++
hasn'tfounditselfinthelowerlayersistheperceptionthatexceptionhandling,name
mangling,linkingandissuesofthatnatureaddunwantedcomplexityorthatsomehowthe
generatedcodeislessefficient.Argumentshavebeenmadethatthisisnotthecase,butthe
perceptionstillremains.
Cstilltendstobemorepopularinlowlevelsystemsprogramming.Componentssuchasthe
LinuxkernelarepureCwithsomeassembly.Manypopularopensourcelibrariessuchas
sqlite3arealsowritteninC.
Objective-C
Objective-CisanotherCderivedlanguagethataddedobjectsandclasses.UnlikeC++,
Objective-CbehavesasastrictsupersetofC.
Thelanguagewasdevelopedinthe1980sandwaspopularizedintheNeXTSTEPoperating
systemandlaterinApple'sOSXandiOS.Ithasn'tgainedmuchpopularityoutsideofthose
platformsbutthesuccessoftheiPhonehasensuredithasasizeabledeveloperbaseofits
own.ItisalsowellsupportedbytheGCCandClangtoolchains.Applehasbegunto
deprecateObjective-CinfavourofSwiftwhichisamodernhighlevellanguagesimilarin
somerespectstoRustbutmoreapplicationfocussed.
Objective-CisstronglyinfluencedbySmalltalk(asopposedtoSimulainC++)andsocode
workssomewhatdifferentlythanC++.
Notionallycodecallsobjectsbysendingthemamessage.Anobjectdefinesaninterface
specifyingwhatmessagesitacceptsandanimplementationthatbindsthosemessagesto
code.Thecallercodesendsamessagetocallamethod.Objectscanalsoreceivedynamic
messages,i.e.onesnotdefinedbytheirinterfaces,sotheycandocertaintaskssuchas
CandC++Background
18

interceptingandforwardingmessages.Inadditionanobjectcanignoreamessageornot
implementitwithoutitbeingconsideredanerror.Inabroadsense,anObjCmessageanda
C++methodareormoreorlessanalogousinfunctionality.
C/C++Timeline
ThesearethemajorrevisionsofCandC++
Year Event Description
1972 C CforPDP-11,otherUnixsystems
1978 K&RC Casdefinedin"TheCProgrammingLanguage"bookby
Kernighan&Ritchie
1989 C89(ANSI
X3.159-1989)
CisstandardizedasANSIC,orC89.C90(ISO/IEC
9899:1990)istheISOratifiedversionofthissame
standard.
1979 Cwithclasses->
C++ BjarneStroustrops
1995 C95(ISO/IEC
9899/AMD1:1995)
Widecharactersupport,digraphs,newmacros,and
someotherminorchanges.
1998 C++98(ISO/IEC
14882:1998) C++isstandardizedforthefirsttime.
1999 C99(ISO/IEC
9899:1999)
Singleline(//)comments,mixingdeclarationswithcode,
newintrinsictypes,inlining,newheaders,variablelength
arrays
2003 C++03(ISO/IEC
14882:2003)
Primarilyadefectrevision,addressingvariousdefectsin
thespecification.
2011 C++11(ISO/IEC
14882:2011)
Amajorrevisionthatintroducestypeinference(auto),
rangebasedloops,lambdas,stronglytypedenums,a
nullptrconstant,structinitialization.Improvedunicode
char16_t,char32_t,u,Uandu8stringliterals.
2011 C11(ISO/IEC
9899:2011)
Multi-threadingsupport.Improvedunicodechar16_t,
char32_t,u,Uandu8stringliterals.Otherminorchanges
2014 C++14(ISO/IEC
14882:2014)
Anothermajorrevisionthatintroducesautoreturntypes,
variabletemplates,digitseparators(1'000'000),generic
lambdas,lambdacaptureexpressions,deprecated
attribute.
CandC++Background
19

RustBackground
ThecatalystforRustwastheMozillaFirefoxwebbrowser.Firefoxlikemostwebbrowsers
is:
WritteninC++.
Complexwithmillionsoflinesofcode.
Vulnerabletobugs,vulnerabilitiesandexploits,manyofwhichareattributabletothe
languagethesoftwareiswrittenin.
Mostlysingle-threadedandthereforenotwellsuitedformany-coredevices-PCs,
phones,tabletsetc.Implementingmulti-threadingtotheexistingenginewould
doubtlesscauseevenmorebugsandvulnerabilitiesthanbeingsinglethreaded.
RustwasconceivedasawaytoobtainCorC++levelsofperformancebutalsoremove
entireclassesofsoftwareproblemthatdestabilizesoftwareandcouldbeexploited.Code
thatpassesthecompilerphasecouldbeguaranteedtobememorysafeandthereforecould
bewritteninawaytotakeadvantageofconcurrency.
SoRustbeganlifeasaresearchprojectbyGraydonHoarein2009fortheMozilla
foundationtosolvetheseissues.Itprogresseduntilthereleaseofversion1.0in2015.
TheprojectishostedonGitHub.Thelanguagehasbeenself-hostingforquitesometime-
thatistosaytheRustcompileriswritteninRust,socompilingRusthappensfroma
compilerwritteninRust.Getyourheadaroundthat!Butit'sthesamewaythatCandC++
compilersarethesedaystoo.
ReadthisMozillainternalstringguidetogetaflavorofthesortofproblemsthebrowser
hadtoovercome.Abrowserobviouslyusesalotoftemporarystrings.STLstringsweretoo
inefficient/flakeyfromonecompilertothenextandsothebrowsersproutedanentiretree
ofstringrelatedclassestosolvethisissue.SimilartalesaretoldinQtandotherlarge
libraries.
ProblemswithC/C++
Itistrivial(byaccident)towritecodethatisinerrorsuchascausingamemoryleak.Itis
easy(bymalice)toexploitbadlywrittencodetoforceitintoerror.Iteasywiththebest
testingintheworldforsomeoftheseerrorstoonlymanifestthemselveswhenthecodeisin
production.
1
1
RustBackground
20

Atbest,bugsareacostlyburdenfordeveloperstofindandfix,notjustintimeanddollars
butalsotheirreputation.Atworst,thebugcouldcausescatastrophicfailurebutmore
ordinarilyleavescodeunstableorvulnerabletohacking.
Rustisalanguagethatproducesmachinecodethatiscomparableinperformanceas
C/C++butenforcesasafe-by-designphilosophy.Simplyput,thelanguageandthecompiler
trytostoperrorsfromhappeninginthefirstplace.Forexamplethecompilerrigorously
enforceslifetimetrackingonobjectsandgenerateserrorsonviolations.Mostofthese
checksandguardsaredoneatcompiletimesothereisazero-costatruntime.
ActiveDevelopment
TheRustteamreleasesanewversionofRustapproximatelyevery6weeks.Thismeans
Rustreceivescodeandspeedimprovementsovertime.
MostreleasesfocusonmarkingAPIsasstable,improvingcodeoptimizationandcompile
times.
Opensourceandfree
RustisduallicensedundertheApache2.0andMITopensourcelicenses.Thefullcopyright
messageisviewableonline.
EssentiallythelicensecoversyourrighttomodifyanddistributetheRustsourcecode.Note
thatRustgeneratescodeforLLVMsoLLVMalsohasitsownsoftwarelicense(TODOlink).
WhatyoucompilewithRust(orLLVM)isnotaffectedbytheopensourcelicense.Soyou
maycompile,executeanddistributeproprietarycodewithoutobligationtotheselicenses.
IsRustforeverybody?
Noofcoursenot.Performanceandsafetyareonlytwothingstoconsiderwhenwriting
software.
Sometimesit'sokayforaprogramtocrasheverysooften
Ifyouhavecodethat'swrittenandworksthenwhythrowthataway?
Writingnewcodewillalwaystakeeffortandwillstillcauseapplicationlevelbugsofone
sortoranother.
Performancemaynotbeabigdealespeciallyfornetworkboundcodeandahigher
levellanguagelikeJava,C#,Gomaysuitbetter.
RustBackground
21

Somepeoplewillfindthelearningcurveextremelysteep.
Rustisstillrelativelyimmatureasalanguageandstillhassomeroughedges-
compilationtimes,optimization,complexmacros.
ButyoumaystillfindthereisbenefittomovingsomeofyourcodetoRust.Forexample,
yourC++softwaremightworkgreatbutithastodealwithalotofuser-generateddataso
perhapsyouwanttoreimplementthatcodepathinRustforextrasafety.
Safebydesign
Someexamplesofthissafe-by-designphilosophy:
Variable(binding)isimmutablebydefault.ThisistheoppositeofC++wheremutableis
thedefaultandwemustexplicitlysayconsttomakesomethingimmutable.Immutability
extendstothe&selfreferenceonstructfunctions.
Lifetimetracking.TheRustcompilerwilltrackthelifetimeofobjectsandcangenerate
codetoautomaticallydropthemwhentheybecomeunused.Itwillgenerateerrorsif
lifetimerulesareviolated.
Borrowing/Variablebinding.Rustenforceswhichvariable"owns"anobjectatany
giventime,andtracksvaluesthataremovedtoothervariables.Itenforcesrulesabout
whomayholdamutableorimmutablereferencetoit.Itwillgenerateerrorsifthecode
triestousemovedvariables,orobtainmultiplemutablereferencestoit.
ThereisnoNULLpointerinsafecode.Allreferencesandpointersarevalidbecause
theirlifetimesandborrowingaretracked.
RustusesLLVMforthebackendsoitgeneratesoptimizedmachinecode.
Lintcheckingisbuiltin,e.g.styleenforcementfornamingconventionsandcode
consistency.
Unittestscanbeintegratedintothecodeandrunautomatically
Modules(equivalenttonamespacesC++)areautomaticmeaningweimplicitlygetthem
byvirtueofourfilestructure.
Don'tC++11/C++14getusthis?
Yesandno.C++11andC++14certainlybringinsomelongoverduechanges.Concurrency
primitives(threadsatlast!),movesemantics,pointerownershipandotherbeneficialthings
allcomeinwiththeselateststandards.Conveniencessuchastypeinference,lambdasetal
alsocomein.
Andperhapsifyouprogramtherightsubsetoffeaturesanddiligentlyworktoavoidpitfallsof
C++ingeneralthenyouaremorelikelytocreatesafecode.
RustBackground
22

Butwhatistherightsubset?
Ifyouusesomeoneelse'slibrary-aretheyusingtherightsubset?
IfonesubsetisrightthenwhydoesC++stillcontainallthestuffthatisoutsideofthat?
Whyareallthethingswhicharepatentlyunsafe/dangerousstillallowed?
Whyarecertaindangerousdefaultbehaviorssuchasdefaultcopyconstructorsnot
flippedtoimprovecodesafety?
WecouldarguethatC++doesn'twanttobreakexistingcodebyintroducingchangethat
requirescodetobemodified.That'sfairenoughbuttheflip-sideisthatfuturecodeisalmost
certainlygoingtobebrokenbythisdecision.Perhapsitwouldbebettertoinflictalittlepain
forsomelongtermgain.
Unsafeprogramming/Cinteroperability
Rustrecognizesyoumayneedtocallanexternallibraries,e.g.inaClibraryorasystem
API.
Thereforeitprovidesan unsafekeywordthatthrowssomeofthesafetyswitcheswhenitis
necessarytotalktotheoutsideworld.
ThisallowsyouconsiderthepossibilityofportingcodepartiallytoRustwhilestillallowing
someofittoremainasC.
RustBackground
23

Let'sStartSimple
Theusualintroductiontoanylanguageis"Hello,World!".Asimpleprogramthatprintsthat
messageouttotheconsole.
HereishowwemightwriteitforC:
#include<stdio.h>
intmain(intargc,char*argv[]){
printf("Hello,World!\n");
return0;
}
C++couldwriteitthesameway,orwecouldusetheC++streamclassesifwepreferred:
#include<iostream>
usingnamespacestd;
intmain(intargc,char*argv[]){
cout<<"Hello,World!"<<endl;
return0;
}
AndhereistheequivalentinRust:
fnmain(){
println!("Hello,World!");
}
Therearesomeobviouspointsofsimilaritythatwecanobserve:
C/C++andRustfollowtheconventionofhavinga main()functionastheentrypoint
intocode.NotethatRust'smaindoesn'treturnanything.It'seffectivelyavoidmethod.
Thereisageneralpurposeprintstatement.
Thegeneralstructureintermsofmain,useof{}andsemi-colonsismostlythesame.In
bothlanguagesablockofcodeisenclosedincurlybraces,andasemi-colonisusedas
Let'sStartSimple
24

aseparatorbetweenstatements.
RustlooksalittlebitmoretersethaneitherCorC++becauseitautomaticallyincludes
referencestopartofitsstandardruntimethatitreferstoasits"prelude".
The println!()isactuallyamacrothatexpandsintocodethatwritestothestandard
output.Weknowit'samacrobecauseitendsina!characterbutyoumaytreatitlikea
functioncallfornow.We'llseehowRustmacrosdiffertothoseinC/C++later.
Compilingourcode
Openacommandpromptandsetupyourcompilerenvironments.
Ifyouwereusinggcc,you’dcompileyourcodelikethis:
gcchw.cpp-ohw
IfyouwereusingMicrosoftVisualC++you'dcompilelikethis:
cl/ohw.exehw.cpp
TocompileinRustyouinvoketherustccompiler.
rustchw.rs
Andtoruneither
./hw(or.\hw.exe)
Hello,World!
Againtherearepointsofsimilarity:
Thereisashellcommandthatcompilesthecodeandcreatesanexecutablefromit.
Thebinaryrunsinthesameway.
AlessobviouspointofsimilarityisthatRustsharesitscodegenerationbackendwithgcc-
llvmandclang.Rustcoutputsllvmbitcodewhichiscompiled(andoptimized)intomachine
codeviaLLVM.Thismeanstheresultingexecutableisverysimilarinformtothatoutputby
C++compilers.Thatincludesthesymbolicinformationitsuppliesfordebuggingpurposes.A
rustexecutablecanbedebuggedingdb,lldborMicrosoftVisualStudiodependingonthe
targetplatform.
Let'sStartSimple
25

rustc-Ohw.rs
Let'sStartSimple
26

CompilingandLinkinginMoreDetail
Yourmain()entrypoint
RusthasamainfunctionjustlikeC/C++whichisusuallycalled main().
Itdoesn’ttakeanyargumentsanditdoesn’treturnanythingunlikeC/C++.Let'sseehowwe
mightdothosethings.
Processingcommand-linearguments
InC/C++,theentrypointtakesargc,andargvarguments.Argcisthenumberofarguments
andargvisanarrayofchar*pointersthatspecifythosearguments.
intmain(intarcg,char**argv){
//ourcode
}
Processingargumentscanbecomeinordinatelycomplex(andbuggy)somostsoftwarewill
useafunctionlike getopt()or getopt_long()tosimplifytheprocess.
Notethat getopt()isnotastandardCfunctionandisnotportable,e.g.toWindows.So
immediatelyweseeanexampleofproblemthatC/C++forcesustosolve.
Rustdoesn'tprocessargumentsthisway.Insteadyouaccessthecommand-lineparameters
from std::env::args()fromanywhereinthecode.Thatistosay,thereisafunctioncalled
args()underthenamespace std::envthatreturnsthestringsonthecommand-line.
Thefunction args()returnstheparametersinastringarray.AswithC++,thefirstelement
ofthearrayatindex0isthecommanditself:
usestd::env;
fnmain(){
forargumentinenv::args(){
println!("{}",argument);
}
}
1
CompilingandLinkinginMoreDetail
27

Alternatively,since args()returnsatypecalled Argsthatimplementsthe Iteratortrait
youcancollecttheargumentsupintoyourowncollectionandprocessthat:
usestd::env;
usestd::collections::HashSet;
fnmain(){
letargs:HashSet<String>=env::args().collect();
letverbose_flag=args.contains("--verbose");
}
WecanseesomeclearadvantagestohowRustsuppliesargs:
Youdon'tneedaseparateargc,parameter.Youhaveanarraythatdefinesitsown
length.
Youcanaccessargumentsfromanywhereinyourprogram,notjustfromthe main().
InC++youwouldhavetopassyourargsaroundfromoneplacetoanother.InRustyou
cansimplyaskforthemfromanywhere.
Useacrate-easycommand-lineprocessing
Rusthasanumberofcratesforprocessingarguments.Themostpopularcratefor
processingargumentsisclap.
Itprovidesaverydescriptive,declarativewayofaddingrulesforprocessingargumentsinto
thecode.Itisespeciallyusefulifyourprogramtakesalotofarguments,including
parametersandvalidationrules.
Forexampleweaddthisto Cargo.toml:
[dependencies]
clap="2.27"
Andinour main.rs.
CompilingandLinkinginMoreDetail
28

#[macro_use]externcrateclap;
useclap::*;
fnmain(){
letmatches=App::new("SampleApp")
.author("MyName<myname@foocorp.com>")
.about("Sampleapplication")
.arg(Arg::with_name("T")
.long("timetowait")
.help("Waitssomeperiodoftimeforsomethingto
happen")
.default_value("10")
.takes_value(true)
.possible_values(&["10","20","30"])
.required(false))
.get_matches();
lettime_to_wait=value_t_or_exit!(matches,"T",u32);
println!("Timetowaitvalueis{}",time_to_wait);
}
Thiscodewillprocessargumentsfor -Tor --timetowaitandensurethevalueisoneof3
accepted.Andiftheuserdoesn'tsupplyavalue,itdefaultsto 10.Andiftheuserdoesn't
supplyavalidintegeritwillterminatetheapplicationwithausefulerror.
Theusercanalsoprovide --helpasanargumentanditwillprintouttheusage.
Exitcode
Ifyouwanttoexitwithacode,yousetitexplicitly:
fnmain(){
//...mycode
std::os::set_exit_status(1);
}
When main()dropsout,theruntimecleansupandreturnsthecodetotheenvironment.
Againthereisnoreasonthestatuscodehastobesetin main(),youcouldsetit
somewhereelseand panic!()tocausetheapplicationtoexit.
CompilingandLinkinginMoreDetail
29

Optimizedcompilation
Inatypicaledit/compile/debugcyclethereisnoneedtooptimizecodeandsoRust
doesn'toptimizeunlessyouaskitto.
Optimizationtakeslongertohappenandcanreorderthecodesothatbacktracesand
debuggingmaynotpointattheproperlinesofcodeinthesource.
Ifyouwanttooptimizeyourcode,adda-Oargumenttorustc:
rustc-Ohw.rs
TheactofoptimizationwillcauseRusttoinvoketheLLVMoptimizerpriortolinking.Thiswill
producefasterexecutablecodeattheexpenseofcompiletime.
Incrementalcompilation
Incrementalcompilationisalsoimportantforedit/compile/debugcycles.Incremental
compilationonlyrebuildsthosepartsofthecodewhichhavechangedthroughmodification
tominimizetheamountoftimeittakestorebuildtheproduct.
RusthasadifferentincrementalcompilationmodeltoC++.
C++doesn'tsupportincrementalcompilationperse.Thatfunctionislefttothemake/
project/solutiontool.Mostbuilderswilltrackalistofprojectfilesandwhichfile
dependsonotherfiles.Soiffilefoo.hchangesthenthebuilderknowswhatotherfiles
dependonitandensurestheyarerebuiltbeforerelinkingthetargetexecutable.
InRustincrementalcompilationisatthecratelevel-thatifanyfileinacratechanges
thenthecrateasawholehastoberebuilt.Thuslargercodebasestendtobesplitup
intocratestoreducetheincrementalbuildtime.
ThereisarecognitionintheRustcommunitythatthecrate-levelmodelcansuckforlarge
cratessotheRustcompilerisgettingincrementalper-filecompilationsupportinadditionto
per-crate.
Atthetimeofwritingthissupportisexperimentalbecauseitistiedtorefactoringthe
compilerforotherreasonstoimproveperformanceandoptimizationbutwilleventuallybe
enabledandsupportedbyrustcandcargo.
Managingaproject
CompilingandLinkinginMoreDetail
30

InC++wewouldusea makefileorasolutionfileofsomekindtomanagearealworld
projectandbuildit.
Forsmallprogramswemightrunascriptorinvokeacompilerdirectlybutasourprogram
growsandtakeslongertobuild,wewouldhavetousea makefiletomaintainoursanity.
Atypical makefilehasrulesthatsaywhatfilesareoursources,howeachsourcedepends
onothersources(likeheaders),whatourfinalexecutableisandabunchofothermess
aboutcompileandlinkflagsthatmustbemaintained.
Therearelotsofdifferentmakefilesolutionswhichhavecroppedupovertheyearsbuta
simplegmakemightlooklikeone:
SRCS=main.opacman.osprites.osfx.o
OBJS=$(SRCS:.cpp=.o)
EXE=pacman
$(EXE):$(OBJS)
$(CC)$(CFLAGS)-o$(EXE)$(OBJS)
.cpp.o:
$(CC)$(CFLAGS)-c$<-o$@
Whenyouinvoke make,thesoftwarewillcheckallthedependenciesofyourtarget,looking
attheirfilestampsanddeterminewhichrulesneedtobeinvokedandwhichordertorebuild
yourcode.
Rustmakesthingsaloteasier–thereisnomakefile!Thesourcecodeisthemakefile.Each
filesayswhatotherfilesitusesviadepenciesonothercrates,andonothermodules.
Considerthismain.rsforapacmangame:
modpacman;
fnmain(){
letmutgame=pacman::Game::new();
game.start();
}
Ifwesavethisfileandtype rustcmain.rsthecompilerwillnoticethereferenceto mod
pacmanandwillsearchfora pacman.rs(or pacman/mod.rs)andcompilethattoo.Itwill
continuedoingthiswithanyothermodulesreferencedalongtheway.
CompilingandLinkinginMoreDetail
31

Inotherwordsyoucouldhaveaprojectwith1000filesandcompileitassimplyas rustc
main.rs.Anythingreferencedisautomaticallycompiledandlinked.
Okay,sowecancall rustc,butwhathappensifourcodehasdependenciesonother
projects.Orifourprojectismeanttobeexportedsootherprojectscanuseit?
Cargo
Cargoisapackagemanagerbuildtoolrolledintoone.Cargocanfetchdependencies,build
them,buildandlinkyourcode,rununittests,installbinaries,producedocumentationand
uploadversionsofyourprojecttoarepository.
TheeasiestwaytocreateanewprojectinRustistousethe cargocommandtodoit
cargonewhello_world–bin
Createsthis
hello_world/
.git/(gitrepo)
.gitignore
Cargo.toml
src/
main.rs
Buildingtheprojectisthensimplyamatterofthis:
cargobuild
Ifyouwanttobuildforreleaseyouadda--releaseargument.Thiswillinvokestherust
compilerwithoptimizationsenabled:
cargobuild--release
Ifwewantedtobuildandrununittestsinourcodewecouldwrite
cargotest
Cratesandexternaldependencies
CompilingandLinkinginMoreDetail
32

Cargodoesn'tjusttakecareofbuildingourcode,italsoensuresthatanythingourcode
dependsonisalsodownloadedandbuilt.Theseexternaldependenciesaredefinedina
Cargo.tomlinourprojectroot.
Wecaneditthatfiletosaywehaveadependencyonanexternal"crate"suchasthe time
crate:
[package]
name="hello_world"
version="0.1.0"
authors=["JoeBlogs<jbloggs@somewhere.com>"]
[dependencies]
time="0.1.35"
Nowwhenwerun cargobuild,itwillfetch"time"fromcrates.ioandalsoanydependencies
that"time"hasitself.Thenitwillbuildeachcrateinturnautomatically.Itdoesthisefficiently
soiterativebuildsdonotincurapenalty.Externalcratesaredownloadandbuiltinyour
.cargohomedirectory.
Touseourexternalcratewedeclareitinthemain.rsofourcode,e.g.
externcratetime;
fnmain(){
letnow=time::PreciseTime::now();
println!("Thetimeis{:?}",now);
}
Sothechangetothe Cargo.tomlandareferenceinthesourceissufficientto:
1. Fetchthecrate(andanydependencies)
2. Buildthecrate(andanydependencies)
3. Compileandlinktothecrateanddependencies
Allthathappenedwithalinein Cargo.tomlandalineinourcodetoreferencethecrate.We
didn'thavetomessaroundfiguringhowtobuildtheotherlibrary,ormaintainmultiple
makefiles,orgettingourcompiler/linkerflagsright.Itjusthappened.
Cargo.lock
CompilingandLinkinginMoreDetail
33

Alsonotethatoncewebuild,cargocreatesa Cargo.lockfileinourrootdirectory.
Thisfileismadesothatif cargobuildisinvokedagainithasanexactlistofwhat
packagesneedtobepulledandcompiled.Itstopssituationswherethecodeunderourfeet
(sotospeak)movesandsuddenlyourprojectnolongerbuilds.Soifthelockfileexists,the
samedependencyconfigurationcanbereproducedevenfromaclean.Ifyouwanttoforce
thecargotorebuildanewlockfile,e.g.afterchanging Cargo.toml,youcantype cargo
update.
.Youcanchangethemainentrypointusingaspecial #[start]directiveifyouwant
onanotherfunctionbutthedefaultismain()↩
1
CompilingandLinkinginMoreDetail
34

SourceLayoutandOtherGeneralPoints
Headerfiles
C/C++
CandC++codetendstobesplitovertwogeneralkindsoffile:
TheHeaderfile(.h,.hpp)containsclassdefinitions,externalfunctionsignatures,
macros,templates,inlinefunctions.Sometimesinlinefunctionsgetstoredintheirown
file.ThestandardtemplatelibraryC++headersdonothaveafileextension.Some3rd
partylibrarieslikeQtmaysometimesomittheextension.
TheSourcefile(.c,.cc,.cpp)containstheimplementationofclassesandanything
private.SometimesC++willusetrickssuchasforwardclassreferencesandPimpl
patternstokeepcomplexordependentcodeoutoftheheaderfile.
Occasionallyyoumayalsoseefileswitha.inl,or.ippextensionwhichareheaderswithalot
ofinlinetemplatesorfunctions.
Compilersareonlyinterestedinsourcefilesandwhatthey #includesowhat'sreally
happeninginmostC/C++codeisthatapreprocessorconcatenatesvariousheaderfilesto
thefrontofthesourcefileaccordingtothe #directiveswithinitandtheresultingfileisfed
toacompiler.
Splittingdefinitionandimplementationacrossmultiplefilescanbeanuisancesinceitmeans
thatchangestoasingleclasscanrequiremodificationstomultiplefiles.
Rust
Rustdoesnothaveheaderfiles.Everystruct,implementationandmacroresidesinafile
endingin.rs.Codeismadepublicornotbystructuring.rsfilesintomodulesandexposing
functionsviathe pubkeyword.
Orderingislessimportanttoo.Itispossibletoforwardreferencestructsorfunctions,oreven
usetheverysamemodulethatapieceofcodeisapartof.Theonlytimethatordering
mattersisformacrodefinitions.Amacromustbedefinedbeforeamodulethatusesit.
Rustfilesreferencenon-dependentmoduleswiththe usekeywordandpull-independent
moduleswiththe modkeyword.
SourceLayoutandOtherGeneralPoints
35

Namespaces
C/C++
Cdoesnotusenamespaces.Librariestendtoprefixtheirfunctionsandstructswitha
qualifyingnameofsomesort.
C++doeshavenamespacesbuttheiruseisoptionalandvariesfromonepieceofcodeto
thenext.
Rust
Rusthasmoduleswhicharelike #includeandnamespacesrolledintoone
Onemajorconveniencedefinitionandimplementationareoneandthesame.Implementing
afunctionbringsitintoexistence.Anyothermodulethatchoosesto"use"itsimplysaysso
andthecompilerwillensureitcompilesproperly.
SeeNamespacingwithmodulesTODOref
Filenameconventions
InC++filenamestypicallyendin:
.h,.hpp,.inlforheadersorinlinecode
.c,.cpp,.ccforsourcecode
Asidefromtheextension(whichmaykickoffthecompilerexpectingCorC++)thereisnext
tonoexpectedarrangementornamingconventionforfiles.
Youcancompileafilecalleddeeply/nested/Timbuktu.cppwhichdefines20classesand30
interfacesifyoulikeandthenamedoesnotmatter.
Rustfilesaresnake_caseandendin.rs.ThefilenameDOESmatterbecausethenameis
themodulenamethatscopeswhateverisinit.Therearealsosomespecialfilescalled
main.rs,lib.rsandmod.rs.
Soifyounameyourfilefoo.rs,theneverythinginsideisscopedfoo::*whenexternally
referenced.
Unicodesupport
SourceLayoutandOtherGeneralPoints
36

UsingUnicodeinC++hasalwaysbeenapain.NeitherCnorC++hadsupportforitatall,
andvarioussolutionshaveappearedovertime.Recentimplementationsofthestandardsof
CandC++providestringliteraltypesforUTFencodings,butpriortothatitwasstrictlyascii
orwidecharacters.
HerearesomegeneralguidelinesforUnicodeinC/C++:
Sourcecodeisnormallyonlysafetousecharacters0-127althoughsomecompilers
mayhaveparametersthatallowmakefilestospecifyotherencodings.
C++hascharandwchar_ttypesfor8-bitand32-bitorpossibly16-bitwidestrings.Part
oftheproblemwithwchar_twasthewidthwasimmediatelysubverted.
Chartypeimpliesnoencoding.ItnormallymeansASCIIbutcouldalsomeanUTF-8,
Latin1,orinfactanyformofencodingthatpredatesUnicode.Basicallyitisuptothe
programtounderstandthemeaning.
A"wide"wchar_tisNOTUTF-32.Itmightbe,oritmightbeUTF-16onsomeplatforms
(e.gWindows).Thismessedupdefinitionmakesoperationssuchasslicingstrings
dangerousduetotheriskofcuttingthroughacontrolpoint.
WhatifIwanttoreadUnicodeargumentsfromthecommand-linesuchasfilepaths-
whatencodingaretheyin?Themain()methodpassesthemaschar.Windowshasa
wmain()thattakeswchar_t.WhatamIsupposedtodo?
Windowsfavourswide(UTF-16)characterstringsforitsAPIsalthoughithasASCII
versionstoo.TheASCIIversionsarenotUTF-8.Compiledcodehas#defineUNICODE
tosupportmultiplelanguages.
LinuxtendstofavourUTF-8encodedcharstrings.Mostlanguages,toolkitsandtools
assumeUTF-8.TheexceptionisQtwhichhaschosentouseUTF-16internally.
C-libhasacquiredvariouswideversionsofitsstrdup,strcmp,strcpyetc.Italso
acquiredwideversionsoffunctionsforopeningfilesondiskandsoforth.
C++libhasacquiredstd::string/std::wstringclasses.C++hasacquiredexplicitUTF-16
andUTF-32versionsoftheseclasses.
C11andC++11introduceexplicitstringliteralsforvariousUTFwidths.
Limitedconversioncapabilitiesbetweenwide/narrowinC++.Someoperatingsystems
haveincompleteconversioncapabilities.
3rdpartyconversionlibrarieslikeICU4Carecommonlyused.Librarieslikeboost,Qt
uselibicuforconvertingbetweenencodings
EmbeddingUnicodeintoCsourceinvolvesusingescapecodesorhexvalues
Rustsimplifiesthingsalotbybenefitofhindsight.
SourcecodeisUTF-8encoded.
Comments,charactersandstringliteralscancontainUnicodecharacterswithout
escaping.
Thenativechartypeis4byteswide–aswideasaUnicodecharacters.
SourceLayoutandOtherGeneralPoints
37

Thenativestr&StringtypesareinternallyUTF-8tosavespacebutmaybeiteratedby
charorbybyteaccordingtowhatthefunctionisdoing.
SincesourcecodeisUTF-8encodedyoumayembedstringsstraightintothesource.
lethello="你好";
forcinhello.chars(){/*iteratechars*/
//...
}
SourceLayoutandOtherGeneralPoints
38

NamespacingWithModules
C++namespacesallowyoutogroupyourfunctions,variablesandclassesintologicalblocks
andallowthecompilertodisambiguatethemfromotherfunctions,variablesandclassesthat
mightotherwisehavethesamename.
//Namespacingisusuallyagoodidea
namespacemyapp{
voiderror(){
//...
}
constintSOME_VALUE=20;
voiddoSomething(intvalue){
//...
}
}
//...somewhereelseinthecode
myapp::doSomething(100);
NamespacinginC++iscompletelyoptionalwhichmeanssomecodemayusenest
namespaceswhileothercodemaybecontenttocoveritsentirecodebasewithasingle
namespace.Somecodemightevenputitscodeintotheglobalnamespace.Othercode
mightcontroltheuseofnamespaceswithmacros.
TheequivalenttoanamespaceinRustisamoduleandservesasimilarpurpose.Unlike
C++thoughyougetnamespacingautomaticallyfromthestructureofyourfiles.Eachfileisa
moduleinitsownright.
Soifwemayhaveafilemyapp.rs
//myapp.rs
pubfnerror(){/*...*/}
pubconstSOME_VALUE:i32=20;
pubfndoSomething(value:i32){/*...*/}
Everythinginmyapp.rsisautomaticallyamodulecalledmyapp.Thatmeansmodulesare
implicitandyoudon'thavetodoanythingexceptnameyourfilesomethingmeaningful.
NamespacingWithModules
39

usemyapp;
myapp::doSomething(myapp::SOME_VALUE);
Youcouldalsojustbringinthewholeofthemodifyoulike:
usemyapp::*;
doSomething(SOME_VALUE);
Orjustthetypesandfunctionswithinitthatyouuse:
usemyapp::{doSomething,SOME_VALUE}
doSomething(SOME_VALUE);
//Otherbitscanstillbereferencedbytheirqualifyingmod
myapp::error();
Butifyouwantanexplicitmoduleyoumayalsowriteitinthecode.Soperhapsmyapp
doesn'tjustifybeingaseparatefile.
//main.rs
modmyapp{
pubfnerror(){/*...*/}
pubconstSOME_VALUE=20;
pubfndoSomething(value:i32){/*...*/}
}
Modulescanbenestedsoacombinationofimplicitmodules(fromfilenames)andexplicit
modulescanbeusedtogether.
Splittingmodulesacrossfiles
Namespacingwithmodulesisprettyeasy,Butsometimesyoumighthavelotsoffilesina
moduleandyoudon'twanttheoutsideworldtoseeasinglemodulenamespace.
Inthesecasesyou’remorelikelytousethemyapp/mod.rsform.Inthisinstancethemod.rs
filemaypull
insubordinatefiles
NamespacingWithModules
40

//myapp/mod.rs
modhelpers;
modgui;
#[cfg(test)]
modtests
//Perhapswewanttheoutsideworldtoseemyapp::Helper
pubusehelpers::Helper;
Inthisexample,themodulepullsinsubmodules helpersand gui.Neitherismarkedas
pubmodsotheyareprivatetothemodule.
Howeverthemodulealsosays pubusehelpers::Helperwhichallowstheoutsideto
reference myapp::Helper.Thusamodulecanactasagatekeepertothethingsit
references,keepingthemprivateorselectivelymakingpartspublic.
Wehaven'tmentionedtheothermodulehere tests.Theattribute #[cfg(test)]indicates
itisonlypulledinwhenaunittestexecutableisbeingbuilt.The cfgattributeisusedfor
conditionalcompliation.
Usingamodule
Modulescanbeusedoncetheyaredefined.
usehelpers::*;
Notethattheusecommandisrelativetothetoplevel mainor libmodule.Soifyou
declarea modhelpersatthetop,thenthecorresponding usehelperswillretrieveit.You
canalsouserelative usecommandswiththe superand selfkeywords.
//TODOs
Modulealiasing
TODO
Externalcrates
NamespacingWithModules
41

TODO
NamespacingWithModules
42

PortingCode
Beforestarting,theassumptionatthispointisyouneedtoportcode.Ifyou'renotsureyou
dotoportcode,thenmaybeyoudon't.Afterall,ifyourC/C++codeworksfine,thenwhy
changeit?
TODOThissectionwillprovideamorerealworldC/C++exampleandportittothe
equivalentRust
Automationtools
Corrode
Corrodeisacommand-linetoolthatcanpartiallyconvertCintoRust.Attheveryleastitmay
spareyousomedrudgeryensuringthefunctionalityisasclosetotheoriginalaspossible.
CorrodewilltakeaCfile,e.g. somefile.cplusanyargumentsfrom gccandproducesa
somefile.rswhichistheequivalentcodeinRust.
ItworksbyparsingtheCcodeintoanabstractsyntaxtreeandthengeneratingRustfrom
that.
InterestinglyCorrodeiswritteninHaskellandmoreinterestinglyiswrittenasaliterate
Haskellsource-thecodeisamarkdowndocumentinterspersedwithHaskell.
Bindgen
BindgenisatoolforgeneratingFFIinterfacesforRustfromexistingCandC++headerfiles.
Youmightfindthisbeneficialifyou'reportingcodefromC/C++,orwritinganewcomponent
thatmustworkwithanexistingcodebase.
BindgenrequiresthatyoupreinstalltheClangC++compilerinordertoparsecodeintoa
structureitcandigest.
Thereadmedocumentationonthesitelinkprovidesmoreinformationoninstallingandusing
thetool.
Experiences
AnumberofwebsitesofferinsightsoftheportingprocessfromCtoRust
PortingCode
43

1. PortingZopflifromCtoRust.Zopfliisalibrarythatperformsgoodbutslowdeflate
algorithm.Itproducessmallercompressedfilesthanzlibwhilestillremainingcompatible
withit.
2. TODO
Tips
Usereferenceswhereveryoucan
TODOreferencesareequivalenttoC++referencesortopointersinC.Passingbyreference
isanefficientwayofpassinganyobjectgreaterthan64-bitsinsizeintoafunctionwithout
relinquishingownership.Insomecases,itisevenagoodideatoreturnbyreferencesince
thecallercanalwaysclonetheobjectiftheyneedto,andmoreoftenthannottheycanjust
usethereferenceprovidingthelifetimesallowforit.
TODOyoudonotneedtousereferencesonintrinstictypessuchasintegers,boolsetc.and
thereisnopointunlessyouintendforthemtobemutableandchange.
Learnmovesemantics
CandC++defaulttocopyonassign,Rustmovesonassignunlessthetypeimplementsthe
Copytrait.ThisiseasilyoneofthemostmindbogglingthingsthatnewRustprogrammers
willencounter.CodethatworksperfectlywellinC++willinstantlyfailinRust.
Thewaytoovercomethisisfirstusereferencesandsecondlydon'tmoveunlessyouintend
fortherecipienttobethenewownerofanobject.
TODOifyouintendfortherecipienttoownacopyofanobjectthenimplementtheClone
traitonyourstruct.Thenyoumaycall .clone()andtherecipientbecomestheownerofthe
cloneinsteadofyourcopy.
Usemodulestonaturallyarrangeyoursource
code
TODO
Usingcompositionandtraits
PortingCode
44

TODORustdoesnotallowyoutoinheritonestructfromanother.Themannerofovercoming
this.
UsingCargo.tomltocreateyourbuildprofiles
UseRustnamingconventionsandformatting
CandC++haveneverhad
ForeignFunctionInterface
TODOfornowreadtheFFIomnibus.
Leavingfunctionnamesunmangled
TODOattributeno_mangle
libc
TODORustprovidesacratewithbindingsforClibraryfunctions.Ifyoufindyourself
receivingapointerallocatedwithmallocyoucouldfreeitwiththecorrespondingcalltofree()
viathebindings.
TODOaddthefollowingtoyour Cargo.toml
[dependencies]
libc="*"
TODOexampleofusinglibc
PortingCode
45

FeaturesofRustcomparedwithC++
RustandC++haveroughlyanalogousfunctionalityalthoughtheyoftengoaboutitin
differentways.
RustbenefitsfromlearningwhatworksinC/C++andwhatdoesn'tandindeedhascherry-
pickedfeaturesfromavarietyoflanguages.ItalsoenjoysacleanerAPIinpartbecause
thingslikeUnicodedictatethedesign.
Thissectionwillcoversuchtopicsastypes,strings,variables,literals,collections,structs,
loopsandsoon.IneachcaseitwilldrawcomparisonbetweenhowthingsareinC/C++and
howtheyareinRust.
AlsobearinmindthatRustcompilestobinarycodeandisdesignedtouseCbinariesand
beusedbyCbinaries.Thereforethegeneratedcodeissimilar,butitisdifferentassource.
FeaturesofRustcomparedwithC++
46

Types
C/C++compilersimplementadatamodelthataffectswhatwidththestandardtypesare.
Thegeneralruleisthat:
1==sizeof(char)<=sizeof(short)<=sizeof(int)<=sizeof(long)<=sizeof(longlong)
Asyoucansee,potentiallyeverythingallthewayto longlongcouldbeasinglebyte,or
therecouldbesomeothercrazydefinition.Inpracticehowever,datamodelscomeinfour
commontypeswhichwillbecoveredinthenextsection.
Forthissection,we'llcoverthemostlikelyanalogoustypesbetweenRustandC/C++.
Types
47

C/C++ Rust Notes
char
i8
(or
u8)
ThesignednessofaC++charcanbesignedorunsigned-
theassumptionhereissignedbutitvariesbytargetsystem.
ARust charisnotthesameasaC/C++ charsinceitcan
holdanyUnicodecharacter.
unsigned
char u8
signed
char i8
shortint i16
unsigned
shortint u16
(signed)
int
i32
or
i16
InC/C++thisisdatamodeldependent
unsigned
int
u32
or
u16
InC/C++thisisdatamodeldependent
(signed)
longint
i32
or
i64
InC/C++thisisdatamodeldependent
unsigned
longint
u32
or
u64
InC/C++thisisdatamodeldependent
(signed)
longlong
int
i64
unsigned
longlong
int
u64
size_t usize usizeholdsnumbersaslargeastheaddressspace
float f32
double f64
long
double f128 f128supportwaspresentinRustbutremovedduetoissues
forsomeplatformsinimplementingit.
bool bool
void () Theunittype(seebelow)
Rust's chartype,is4byteswide,enoughtoholdanyUnicodecharacter.Thisis
equivalenttothebelated char32_tthatappearsinC++11torectifytheabusedC++98
wchar_ttypewhichonoperatingsystemssuchasWindowsisonly2byteswide.Whenyou
iteratestringsinRustyoumaydosoeitherbycharacteror u8,i.e.abyte.
Seethenextsectiontoforadiscussionondatamodels.
1
2
2
2
2
3
1
2
3
Types
48

Rusthasaspecificnumerictypeforindexingonarraysandcollectionscalled usize.A
usizeisdesignedtobeabletoreferenceasmanyelementsinanarrayasthereis
addressablememory.i.e.ifmemoryis64-bitaddressablethenusizeis64-bitsinlength.
Thereisalsoasigned isizewhichislessusedbutalsoavailable.
Datamodel
ThefourcommondatamodelsinC++are:
LP32- intis16-bit, longandpointersare32-bit.Thisisanuncommonmodel,a
throw-backtoDOS/Windows3.1
ILP32- int, longandpointersare32-bit.UsedbyWin32,Linux,OSX
LLP64- intand longare32-bit, longlongandpointersare64-bit.UsedbyWin64
LP64- intis32-bit, long/ longlongandpointersare64-bit.UsedbyLinux,OSX
C/C++typescomparedtoRust
C/C++andRustwillsharethesamemachinetypesforeachcorrespondinglanguagetype
andthesamecompiler/backendtechnology,i.e.:
1. Signedtypesaretwo'scomplement
2. IEE754-2008binary32andbinary64floatingpointsforfloatanddoubleprecisiontypes.
stdint.h/cstdint
Cprovidesa <stdint.h>headerthatprovidesunambigioustypedefswithlengthand
signedess,e.g. uint32_t.TheequivalentinC++is <cstdlib>.
Ifyouusethetypesdefinedinthisheaderfilethetypesbecomedirectlyanalogousand
unambiguousbetweenC/C++andRust.
3
Types
49

C/C++ Rust
int8_t i8
uint8_t u8
int16_t i16
uint16_t u16
uint32_t u32
int32_t i32
int64_t i64
uint64_t u64
Integertypes
C++
C/C++hasprimitivetypesfornumericvalues,floatingpointvaluesandbooleans.Stringswill
bedealtinaseparatesection.
Integertypes( char, short, int, long)comein signedand unsignedversions.
A charisalways8-bits,butforhistoricalreasons,thestandardsonlyguaranteetheother
typesare"atleast"acertainnumberofbits.Soan intisordinarily32-bitsbutthestandard
onlysayitshouldbeatleastaslargeasa short,sopotentiallyitcouldbe16-bits!
MorerecentversionsofCandC++providea <cstdint>(or <stdint.h>forC)with
typedefsthatareunambiguousabouttheirprecision.
Eventhough <stdint.h>canclearuptheambiguities,codefrequentlysacrifices
correctnessforterseness.Itisnotunusualtoseean intusedasatemporaryincremental
valueinaloop:
strings=read_file();
for(inti=0;i<s.size();++i){
//...
}
While intisunlikelytofailformostloopsinamoderncompilersupportingILP32or
greater,itisstilltechnicallywrong.InaLP32datamodelincrementing32767byonewould
become-32768sothisloopwouldneverterminateif s.size()wasavaluegreaterthan
that.
Types
50

Butlookagainatthissnippet.Whatifthefilereadby read_file()isoutsideofourcontrol.
Whatifsomeonedeliberatelyoraccidentallyfeedsusafilesolargethatourloopwillfail
tryingtoiterateoverit?Indoingsoourcodeishopelesslybroken.
Thisloopshouldbeusingthesametypereturnedfrom string::size()whichisanopaque
unsignedintegertypecalled size_type.Thisisusuallyatypedeffor std::size_tbutnot
necessarily.Thuswehaveatypemismatch.A stringhasaniteratorwhichcouldbeused
insteadbutperhapsyouneedtheindexforsomereason,butitcanmessy:
strings=read_file();
for(string::iteratori=s.begin();i!=s.end();++i){
string::difference_typeidx=std::distance(s.begin(),i);
//...
}
Nowwe'veswappedfromoneopaquetype size_typetoanothercalled difference_type.
Ugh.
C/C++typescanalsobeneedlesslywordysuchas unsignedlonglongint.Again,thissort
ofpufferyencouragescodetomakebadassumptions,usealesswordytype,orbloatthe
codewithtypedefs.
Rust
Rustbenefitsfromintegertypesthatunambiguouslydenotetheirsignednessandwidthin
theirname- i16, u8etc.
Theyarealsoextremelytersemakingiteasytodeclareandusethem.Forexamplea u32
isanunsigned32-bitinteger.An i64isasigned64-bitinteger.
Typesmaybeinferredorexplicitlyprefixedtothevalue:
letv1=1000;
letv2:u32=25;
letv3=126i8;
Rustalsohastwotypescalled usizeand isizerespectively.Theseareequivalentto
size_tinthattheyareaslargeenoughtoholdasmanyelementsasthereisaddressable
memory.Soina32-bitoperatingsystemtheywillbe32-bitsinsize,ina64-bitoperating
systemtheywillbe64-bitsinsize.
Types
51

Rustwillnotimplicitlycoerceanintegerfromonesizetoanotherwithoutexplicituseofthe
askeyword.
letv1=1000u32;
letv2:u16=v1asu16;
Realtypes
C++
C/C++hasfloat,doubleandlongdoubleprecisionfloatingpointtypesandtheysufferthe
samevaguenessasintegertypes.
float
double-"atleastasmuchprecisionasa float"
longdouble-"atleastasmuchprecisionasa double"
Inmostcompilersandarchitectureshoweverafloatisa32-bitsingleprecisionvalue,anda
doubleisan64-bitdoubleprecisionvalue.Themostcommonmachinerepresentationisthe
IEEE754-2008format.
Longdouble
The longdoublehasprovenquiteproblematicforcompilers.Despiteexpectationsthatitis
aquadrupleprecisionvalueitusuallyisn't.Somecompilerssuchasgccmayoffer80-bit
extendedprecisiononx86processorswithafloatingpointunitbutitisimplementation
definedbehaviour.
TheMicrosoftVisualC++compilertreatsitwiththesameprecisionasa double.Other
architecturesmaytreatitasquadrupleprecision.Thefundamentalproblemwith long
doubleisthatmostdesktopprocessorsdonothavetheabilityinhardwaretoperform128-
bitfloatingpointoperationssoacompilermusteitherimplementitinsoftwareornotbother.
Mathfunctions
The <math.h>Cheaderprovidesmathfunctionsforworkingwithdifferentprecisiontypes.
Types
52

#include<math.h>
constdoublePI=3.1415927;
doubleresult=cos(45.0*PI/180.0);
//..
doubleresult2=abs(-124.77);
//..
floatresult3=sqrtf(9.0f);
//
longdoubleresult4=powl(9,10);
Notehowdifferentcallsarerequiredaccordingtotheprecision,e.g.sinf,sinorsinl.C99
suppliesa"type-generic"setofmacrosin <tgmath.h>whichallowssintobeused
regardlessoftype.
C++11providesa <cmath>thatusesspecialisedinlinefunctionsforthesamepurpose:
#include<cmath>
floatresult=std::sqrt(9.0f);
Rust
Rustimplementstwofloatingpointtypes- f32and f64.Thesewouldbeanalogoustoa
32-bit floatand64-bit doubleinC/C++.
letv1=10.0;
letv2=99.99f32;
letv3=-10e4f64;
UnlikeinC/C++,themathfunctionsaredirectlyboundtothetypeitselfprovidingyou
properlyqualifythetype.
letresult=10.0f32.sqrt();
//
letdegrees=45.0f64;
letresult2=angle.to_radians().cos();
Types
53

Rustdoesnothavea128-bitdouble.A f128didexistforaperiodoftimebutwasremoved
toportability,complexityandmaintenanceissues.Notehow longdoubleistreated(ornot)
accordingtothecompilerandtargetplatform.
AtsomepointRustmightgetaf128orf80butatthistimedoesnothavesuchatype.
Booleans
A bool(boolean)typeinC/C++canhavethevalue trueor false,howeveritcanbe
promotedtoanintegertype(0== false,1== true)andaboolevenhasa++operator
forturningfalsetotruealthoughithasno--operator!?
Butinvertingtruewitha!becomesfalseandviceversa.
!false==true
!true==false
Rustalsohasa booltypethatcanhavethevalue trueor false.UnlikeC/C++itisa
truetypewithnopromotiontointegertype
void/Unittype
C/C++uses voidtospecifyatypeofnothingoranindeterminatepointertosomething.
//Afunctionthatdoesn'treturnanything
voiddelete_directory(conststd::string&path);
//Indeterminatepointeruse
structfile_stat{
uint32_tcreation_date;
uint32_tlast_modified;
charfile_name[MAX_PATH+1];
};
//mallocreturnsavoid*whichmustbecasttothetypeneed
file_stat*s=(file_stat*)malloc(sizeof(file_stat));
//Butcastingisnotrequiredwhengoingbacktovoid*
free(s);
Types
54

Thenearestthingto voidinRustistheUnittype.It'scalledaUnittypebecauseit'stypeis
()andithasonevalueof ().
Technically voidisabsolutelynothingand ()isasinglevalueoftype ()sothey'renot
analogousbuttheyserveasimilarpurpose.
Whenablockevaluatestonothingitreturns ().Wecanalsouseitinplaceswherewe
don'tcareaboutoneparameter.e.g.saywehaveafunction do_action()thatsucceedsor
failsforvariousreasons.Wedon'tneedanypayloadwiththeOkresponsesospecify ()
asthepayloadofsuccess:
fndo_action()->Result<(),String>{
//...
Result::Ok(())
}
letresult=do_action();
ifresult.is_ok(){
println!("Success!");
}
Emptyenums
Rustdoeshavesomethingcloser(butnotthesameas) void-emptyenumerations.
enumVoid{}
Essentiallythisenumhasnovaluesatallsoanythingthatassignsormatchesthisnothing-
nessisunreachableandthecompilercanissuewarningsorerrors.Ifthecodehadused
()thecompilermightnotbeabletodeterminethis.
Tuples
Atupleisacollectionofvaluesofthesameordifferenttypepassedtoafunctionorreturned
byoneasifitwereasinglevalue.
C/C++hasnoconceptofatupleprimitivetype,howeverC++11canconstructatupleusing
atemplate:
Types
55

std::tuple<std::string,int>v1=std::make_tuple("Sally",25);
//
std::cout<<"Name="<<std::get<0>(v1)
<<",age="<<std::get<1>(v1)<<std::endl;
Rustsupportstuplesaspartofitslanguage:
letv1=("Sally",25);
println!("Name={},age={}",v1.0,v1.1);
Asyoucanseethisismoreterseandmoreuseful.Notethatthewayatupleisindexedis
differentfromanarraythough,valuesareindexedvia.0,.1etc.
Tuplescanalsobereturnedbyfunctionsandassignmentoperatorscanignoretuple
memberswe'renotinterestedin.
let(x,y,_)=calculate_coords();
println!("x={},y={}",x,y);
//...
pubfncalculate_coords()->(i16,i16,i16){
(11,200,-33)
}
Inthisexample,thecalculate_coords()functionreturnsatuplecontainingthree i16values.
Weassignthefirsttwovaluesto xand yrespectivelyandignorethethirdbypassingan
underscore.Theunderscoretellsthecompilerwe'reawareofthe3rdvaluebutwejustdon't
careaboutit.
Tuplescanbeparticularlyusefulwithcodeblocks.Forexample,let'ssaywewanttoget
somevaluesfromapieceofcodethatusesaguardlockonareferencecountedservice.
Wecanlocktheserviceintheblockandreturnallthevaluesasatupletotherecipients
outsideoftheblock:
Types
56

letprotected_service:Arc<Mutex<ProtectedService>>=
Arc::new(Mutex::new(ProtectedService::new()));
//...
let(host,port,url)={
//LockandacquireaccesstoProtectedService
letprotected_service=protected_service.lock().unwrap();
lethost=protected_service.host();
letport=protected_service.port();
leturl=protected_service.url();
(host,port,url)
}
Thiscodeisreallyneat-thelockallowsustoobtainthevalues,thelockgoesoutofscope
andthevaluesarereturnedinonego.
Arrays
Anarrayisafixedsizelistofelementsallocatedeitheronthestackortheheap.
E.gtocreatea100elementarrayof doublevaluesinC++:
//Stack
doublevalues[100];
//Heap
double*values=newdouble[100];
delete[]values;
//C99stylebraceenclosedlists
doublevalues[100]={0};//Setallto0
doublevalues[100]={1,2,3};//1,2,3,0,0,0,0...
//C99withdesignator
doublevalues[100]={1,2,3,[99]99};//1,2,3,0,0,0,...,0,99
AndinRust:
Types
57

//Stack
letmutvalues=[0f64;100];//100elements
letmutvalues=[1f64,2f64,3f64];//3elements1,2,3
//Heap
letmutvalues=Box::new([0f64;100]);
NotehowRustprovidesashorthandtoinitialisethearraywiththesamevalueorassignsthe
arraywitheveryvalue.InitialisationinCandC++isoptionalhoweveritismoreexpressivein
thatportionsofthearraycanbesetornotsetusingenclosedlistsyntax.
Rustactuallyforcesyoutoinitialiseanarraytosomething.Attemptingtodeclareanarray
withoutassigningitavalueisacompilererror.
Slices
Asliceisaruntimeviewofapartofanarrayorstring.Asliceisnotacopyofthearray/
stringratherthatitisareferencetoaportionofit.Thereferenceholdsapointertothe
startingelementandthenumberofelementsintheslice.
letarray=["Mary","Sue","Bob","Michael"];
println!("{:?}",array);
letslice=&array[2..];
println!("{:?}",slice);
Thisslicerepresentstheportionofarraystartingfromindex2.
["Mary","Sue","Bob","Michael"]
["Bob","Michael"]
Sizeofthearray
CandC++basicallygivenoeasywaytoknowthelengthofthearrayunlessyou
encapsulatethearraywitha std::arrayorhappentorememberitfromthecodethat
declaresit.
//C++11
std::array<Element,100>elements;
std::cout<<"Sizeofarray="<<elements.size()<<std::endl;
Types
58

The std::arraywrapperisoflimitedusebecauseyoucannotpassarraysofanunknown
sizetoafunction.Thereforeevenwiththistemplateyoumaypassthearrayintoafunction
asoneargumentanditssizeasanother.
Alternativelyyoumightseecodelikethis:
constsize_tnum_elements=1024;
charbuffer[num_elements];
//...
//fill_bufferneedstobetoldhowmanyelementsthereare
fill_buffer(buffer,num_elements);
Orlikethis
Elementelements[100];
//...
intnum_elements=sizeof(elements)/sizeof(Element);
InRust,thearrayhasafunctionboundtoitcalled len().Thisalwaysprovidesthelength
ofthearray.Inadditionifwetakeasliceofthearray,thatalsohasa len().
letbuffer:[u8;1024]
println!("Bufferlength={}",buffer.len());
fill_buffer(&buffer[0..10]);
//...
fnfill_buffer(elements:&[Element]){
println!("Numberofelements={}",elements.len());
}
Types
59

Strings
StringsinC++areabitmessythankstothewaylanguagesandcharactershavebeen
mappedontobytesindifferentways.Theexplanationforthisrequiressomebackstory...
Whatisacharacterexactly?
HistoricallyinCandC++,achartypeis8-bits.Strictlyspeakingacharissignedtype
(usually-128to127),butthevaluesessentiallyrepresentthevalues0-255.
TheUS-ASCIIstandardusesthefirst7-bits(0-127)toassignvaluestoupperandlower
caselettersintheEnglishalphabet,numbers,punctuationmarksandcertaincontrol
characters.
Itdidn'thelptherestoftheworldwhousedifferentcharactersets.AndevenASCIIwas
competingwithanotherstandardcalledEBDICwhichwasfoundonmainframecomputers.
Whatabouttheuppervaluesfrom128-255?Someoperatingsystemscameupwitha
conceptcalleda"codepage".Accordingtowhat"codepage"wasineffect,thesymbolthat
theuserwouldseeforacharacterinthe128-255rangewouldchange.
Buteventhisisnotenough.SomelanguageslikeChinese,Japanese,Korean,Thai,Arabic
etc.havethousandsofsymbolsthatmustbeencodedwithmorethanonebyte.Sothefirst
bytemightbeamodifierthatcombineswithfurtherbytestorenderasasymbol.For
exampleMicrosoft'scodepage932useanencodingcalledShiftJIS(Japanese)where
somesymbolsaretwobytes.
Obviouslythiswasrapidlybecomingamess.Eachcodepageinterprettedthesamebyte
arraydifferentlyaccordingtosomeexternalsetting.Soyoucouldnotsendafilewrittenin
Chinesetosomeonewithadifferentcodepageandexpectittorenderproperly.
Unicodetotherescue
TheUnicodestandardassignseveryprintablesymbolinexistencewithaunique32-bit
value,calledacodepoint.Mostsymbolsfallinthefirst16-bitscalledtheBasicMultilingual
Plane(BMP).
Chinahasmandatedallsoftwaremustsupportall32-bits.We'llseehowthishasbecomea
majorheadacheforCandC++
Strings
60

C/C++
Thereisnostringprimitive
CandC++doesnothaveastringprimitivetype,insteadithas chartype,thatisonebyte.
A"string"isapointertoanarrayofcharsthatareterminatedwithazerobyte, '\0'.
//Thearraythatmy_stringpointsatendswithahidden\0
char*my_string="Thisisasclosetoastringprimitiveasyou
canget";
InC,functionssuchas strlen(), strcpy(), strdup()etc.allowstringstobemanipulated
buttheyworkbyusingthezerobytetofigureoutthelengthofthings.So strlen()the
numberofbytesthatwereencounteredbeforea \0wasfound.Sicnethesefunctionsrun
untiltheyfindaterminatingcharacteritisveryeasytoaccidentallyforthemtooverruna
buffer.
InC++the std::stringclasswrapsacharpointerandprovidessafemethodsfor
modifyingthestringinasafemanner.ItisavastimprovementoverCbutitisstillnota
primitive-itisaclassdefinedinaheaderthatiscompiledandlinkedtotheexecutablejust
likeeveryotherclass.
Inaddition,a std::stringwillusuallyuseheaptostorethestring'sdatawhichcanhave
repercussionsformemoryusageandfragmentation.Thereisusuallyahiddencostto
assigningonestringtoanotherbecausememorymustbeallocatedtoreceiveacopyofthe
string,evenifthestringitselfisnotmodifiedduringtheassignment.
Unicodesupport
C/C++addedUnicodesupportbycreatingawidecharactercalled wchar_t.AndC++has
anequivalent std::wstring.
We'resortednowright?
Oopsno,because wchar_ttypecanbeeither2or4byteswideandisacompiler/platform
specificdecision.
InMicrosoftVisualC++thewidecharisan unsignedshort(correspondingtoWin32's
UnicodeAPI),ingccitcanbe32-bitsor16-bitsaccordingtothecompileflags.
A16-bitvaluewillholdsymbolsfromtheBasicMultilingualPlanebutnotthefull32-bit
range.Thismeansthat16-bitwidestringsshouldbeassumedtobeUTF-16encoded
becausetheycannotsupportUnicodeproperlyotherwise.
Strings
61

C++11rectifiesthisbyintroducingexplicit char16_tand char32_ttypesand
correspondingversionsofstringcalled std::u16stringand std::u32string.
Charactertypes
SonowC++has4charactertypes.Greathuh?
Charactertype Encoding
char C,ASCII,EBDIC,UTF-8,adhoc,???
wchar_t UTF-16orUTF-32
char16_t UTF-16
char32_t UTF-32
Rust
Rusthasbeenratherfortunate.Unicodeprecededitsoitmakesaverysimpledesign
choice.
A chartypeisa32-bitUnicodecharacter,alwaysenoughtoholdasinglecharacter.
A strtypeisaUTF-8encodedstringheldinmemory.Codetendstouse&strwhichis
astringslice,basicallyareferencetothestr,oraportionofit.Astrdoesnotneedtobe
terminatedwithazerobyteandcancontainzerobytesifitwants.
A std::Stringisaheapallocatedstringtypeuseformanipulatingstrings,building
them,readingthemfromfile,cloningthemetc.
NotethatinternallyUTF-8isusedforencodingyetacharis32-bits.Thelengthofastrings
isconsideredtobeitsbytelength.Therearespecialiteratorsforwalkingthestringand
decodingUTF-8into32-bitcharacters.
Finallythereisaplatformspecifictype OSStringthathandlesanydifferencesinhowthe
operatingsystemseesstringscomparedtoRust.
TypesComparison
Strings
62

C++ Rust
char*or wchar_t*
C++11- char16_t*, char32_t* str, &str
std::string, std::wstring
std::u16stringstd::u32string std::String
char*vsstr
C/C++donothaveastringprimitive.Astringisapointertosomebytesinmemorythatare
nulterminated.Thesameappliesforwiderchars,exceptofcoursetheyrequire2or4bytes.
//Thetraditionalway
char*my_str="Hello";//Terminateswith\0
//or
charmy_str[]="Hello";//Terminateswith\0
//orwidestringwithLprefix
wchar_thello_chinese=L"\u4f60\u597d";
//C11andC++11addUTFstringliteralprefixes
autohello_8=u8"\u4f60\u597d";//UTF-8encoded
autohello_16=u"\u4f60\u597d";//UTF-16
autohello_32=U"\u4f60\u597d";//UTF-32
Rustwouldusea strforthispurpose.A strisanimmutablearrayofbytessomewhere
inmemory.The strcouldbeontheheapwhenitpointstoa Stringobject,oritcouldbe
inglobalmemoryifthestringisstatic.Astrsliceis &str,isreferencetoastrwhichalso
containsalengthvalue.
letmy_str="Hello";
lethello_chinese="你好";
Typeinferencesfortheseassignmentswillcreateastringslicepointingtothestatically
allocatedstringdata.Thedataitselfdoesn'tmoveandthe &strisread-only.
WecanalsoobservethatRustremovesthemessofcharacterwidthandliteralprefixesthat
CandC++havetosufferunderbecauseUnicodecharactersareimplicitlysupported.
The strhasfunctionsforiteratingoverthestringinbytes/characters,splitting,finda
patternetc.
Strings
63

letmy_str="Hello";//visa&’staticstr
println!("Mystringis{}anditis{}byteslong",v,v.len());
Note len()isthelengthinbytesbecausestringsareUTF-8encoded.Asinglecharacter
maybeencodedas1,2,3,or4bytes.Itmaynotbethenumberofcharactersahuman
wouldactuallysee.
letmy_str="你好";
println!("Numberofbytes={}",my_str.len());
println!("Numberofchars={}",my_str.chars().count());
Numberofbytes=6
Numberofchars=2
Youcansplita &strtoproducealeftandaright &strslicelikethis:
let(part1,part2)="Hello".split_at(3);
println!("Part1={}",part1);
println!("Part2={}",part2);
Part1=Hel
Part2=lo
std::basic_string(C++)vsstd::String(Rust)
ThestandardC++libraryalsohastemplateclass std::basic_stringthatactsasawrapper
aroundthevariouscharactertypesandcanbeusedformanipulatingastringofanywidth.
Thistemplateisspecialisedas
std::string, std:wstring, std::u16stringand std::u32string.
Strings
64

std::stringmy_str="Hello";
my_str+="world";
//C++11alsoallowssometypeinferencewithautos
autos1="Hello"s;//std::string
autos2=u8"Hello"s;//std::string,forcesUTF-8encoding
autos3=L"Hello"s;//std::wstring
autos4=u"Hello"s;//std::u16string
autos5=U"Hello"s;//std::u32string
InRust,the std::Stringtypeservesthesamepurpose:
letv=String::from("Hello");
v.push_str("world");
Usingitisfairlysimple
letmutv=String::from("ThisisaString");
v.push_str("thatwecanmodify");
A Stringhasfunctionstodoactionssuchasappending,e.g.
letb=String::from("Bananas");
letmutresult=String::new();
result.push_str("Apples");
result.push('&');//Pushachar
result.push_str(b.as_str());
println!("result={}",result);
StringsarealwaysvalidUTF-8.
InternallyaStringhasapointertothedata,itslengthandacapacity(maxsize).Ifyou
intendtoexpandastring,thenyoushouldensurethe Stringhassufficientcapacityto
accommodateitslongestvalueotherwiseyoumaycauseittoreallocateitselfexcessively.
Stringswillnevershrinktheircapacityunlessyouexplicitlycall shrink_to_fit().This
meansifyouuseatemporarystringinaloop,it'sprobablybesttoplaceitoutsidetheloop
andreservespacetomakeitefficient.
Strings
65

letmutv=String::with_capacity(100);
//or
letmutv=String::new();
v.reserve_exact(100);
Stringsalsohaveallthemethodsofstrthankstoimplementing Dereftrait.
Formattingstrings
StringscanbeformattedinCwith printfor sprintforinC++composedwithstream
operators,e.g.toa std::stringstream.
Rustuses format!and println!macrosthatmoreresemblethe sprintfmodel.
C++
Rust
formatting
trait
Purpose
%s,
%u,
%d,
%i,
%f,
%c
{}
C/C++requirethetypeoftheparametertobespecified.In
Rustthetypeisinferredand {}willinvokedthetype's
Displaytraitregardlessofwhatitis.SoaStringoutputsits
text,numerictypesreturntheirvalue,booleanasreturnstrue
orfalse,andsoon.
%lld,
%llu {}
C/C++hasextensionstodealwithdifferentsizeintsand
floats,e.g.llforlonglongduetothewayargumentsare
passedtothefunction.InRust,thereisnoneedforthat.
{:?},
{:#?}
InRust {:?}returnswhateverisimplementedbyatype's
Debugtrait.The {:#?}variantcanbeusedtopretty-printthe
outputfortypesthatderivetheDebugtrait.
%-10s {:<10} Formatleftalignedstringpaddedtominimumof10spaces
%04 {:04} Padanumberwithzero'stoawidthof4
%.3 {:.3} Padanumber'sprecisionto3decimalplaces.Mayalsobe
zero-padded,e.g.{:.03}
%e,
%E
{:e},
{:E} Exponentinloweroruppercase
%x,
%X
{:x},
{:X}
Hexadecimalinloweroruppercase.Note {:#x}, {:#X}
prefixestheoutputwith0x
%o {:o} Octal.Note {:#o}prefixestheoutputwith0o
{:b} Binary.Note {:#b}prefixestheoutputwith0b
%p {:p} Presentsastruct'smemorylocation,i.e.pointer
Strings
66

Rusthasmanymoreformattingtraitsthanthis.
Forexampleitallowsnamedparameterslikethisexample:
letmessage=format!("Thetemperature{temp}Ciswithin
{percent}ofmaximum",temp=104,percent=99);
Namedparameterswouldbeparticularlyusefulforlocalizationwheretheorderofvalues
maybedifferentinonelanguagecomparedtoanother.
DisplayandDebugtraits
Rustallowstypestobeformattedasstringsbasedupontheformattingtraitsthey
implement.
Thetwomainimplementationtraitsare:
Display-thisisforstandardtextualrepresentationofatype.
Debug-thisisforthedebuggingtextualrepresentationofatype.Itmightpresent
additionalinformationorbeformattedseparatelytotheDisplaytrait.Itispossibleto #
[derive(Debug)]thistraitwhichisusuallyenoughforthepurposeofdebugging.
Ifwelookatthetraitswecanseethey'reidentical
//std::fmt::Display
pubtraitDisplay{
fnfmt(&self,&mutFormatter)->Result<(),Error>;
}
//std::fmt::Debug
pubtraitDebug{
fnfmt(&self,&mutFormatter)->Result<(),Error>;
}
Alloftheintrinsictypesimplement Displayand Debug.Wecanexplicitlyimplement
Displayonourownstructstoo:
Strings
67

usestd::fmt::{self,Formatter,Display};
structPerson{
first_name:String,
last_name:String,
}
implDisplayforPerson{
fnfmt(&self,f:&mutFormatter)->fmt::Result{
write!(f,"{}{}",self.first_name,self.last_name)
}
}
//...
letperson=Person{first_name:"Susan".to_string(),
last_name:"Smith".to_string()};
println!("Person-{}",person);
Person-SusanSmith
Implementing Debugisusuallydoneby #[derive(Debug)]butitcouldalsobeimplemented.
Thederived Debugwillprintoutthestructname,andthenthemembersincurlybraces:
#[derive(Debug)]
structPerson{
//...
}
//...
println!("Person-{:?}",person);
Person-Person{first_name:"Susan",last_name:"Smith"}
Manytypesprocessformattingtraitswhicharevaluesheldbetweenthe {}bracesinthe
string.ThesearefairlysimilartothepatternsusedinCfunctionsforprintf,sprintfetc.
OsString/OsStr
Strings
68

Rustrecognisestherearetimeswhenyouneedtopassorreceiveastringfromasystem
API.
Inthiscaseyoumayuse OsStringwhichallowsinterchangebetweenRustandasystem
dependentrepresentationsofstrings.OnWindowsitwillreturnUTF-16strings,onLinux/
UnixsystemsitwillreturnUTF-8.
An OsStrisasliceonto OsString,analogousto strand String.
Strings
69

Variables
C++
TypeInference
C++11hastypeinference,previousversionsofC++donot.Typeinferenceallowsthe
programmertoassignavaluetoan autotypedvariableandletthecompilerinferthetype
basedontheassignment.
Booleanandnumerictypesarefairlyeasytounderstandprovidingthecodeisasexplicitas
itneedstobe.
autox=true;//bool
autoy=42;//int
autoz=100.;//double
WhereC++getsmessyisforarraysandstrings.Recallthatstringsarenotprimitivetypesin
thestrongsensewithinCorC++soautorequirestheybeexplicitlydefinedorthetypewill
bewrong.
autos=std::string("Nowisthewindowofourdiscontent");//
charstring
autos=U"BattleofWaterloo";//char32_tpointertoUTF-32
stringliteral
Stringsarecoveredelsewhere,butessentiallytherearemanykindsofstringsandC++/C
hasgrownawholebunchofstringprefixestodealwiththemall.
Arraysareamoreinterestingproblem.The autokeywordhasnoeasywaytoinferarray
typesoisonehackworkaroundtoassignatemplatizedarraytoan autoandcoerceit.
template<typenameT,intN>usingraw_array=T[N];
autoa=raw_array<int,5>{};
Rust
Variables
70

Rust,variablesaredeclaredwitha letcommand.The letmayspecifythevariable's
type,oritmayalsousetypeinferencetoinferitfromthevalueitisassignedwith.
letx=true;//x:bool
lety=42;//y:i32
letz=100.0;//z:f64
letv=vec![10,20,30];//v:Vec<i32>
lets="Nowisthewinterofourdiscontent".to_string();//s:
String
lets2="BattleofWaterloo";//s2:&str
leta1:[i32;5]=[1,2,3,4,5];
Rusthasnoproblemwithusingtypeinferenceinarrayassignments:
leta2=["Mary","Fred","Sue"];
Notethatallarrayelementsmustbethesametype,inferencewouldgenerateacompiler
errorforanarraylikethis:
//Compileerror
leta3=["Mary",32,true];
Scoperules
ScoperulesinC,C++andRustarefairlysimilar-thescopethatyoudeclaretheitem
determinesitslifetime.
Shadowingvariables
OneveryusefulfeatureofRustisthatyoucandeclarethesamenamedvariablemorethan
onceinthesamescopeornestedscopesandthecompilerdoesn'tmind.Infactyou'lluse
thisfeaturealot.
Thisiscalledshadowingandworkslikethis:
Variables
71

letresult=do_something();
println!("Gotresult{:?}",result);
ifletSome(result)=result{
println!("Wegotaresultfromdo_something");
}
else{
println!("Wedidn'tgetaresultfromdo_something");
}
letresult=do_something_else();
//...
Thisexampleusesthevariablename result3times.Firsttostoretheresultofcalling
do_something(),thentoextractsomevalue Foofrom Option<Foo>andathirdtimefor
callingsomethingelse.Wecouldhaveassigned resultto result2andthenlateron
assignedthevalue do_something_else()to result3butwedidn'tneedtobecauseof
shadowing.
Pointers
InC++
Apointerisavariablethatpointstoanaddresssomewhereinmemory.Thepointer'stype
indicatestothecompilerwhattoexpectattheaddressbutthereisnoenforcementto
ensurethattheaddressactuallyholdsthattype.Apointermightmightbeassigned NULL
(or nullptrinC++11)ormayevenbegarbageifnothingwasassignedtoit.
char*name="DavidJones";
intposition=-1;
find_last_index("findtheletterl",'l',&position);
Generallypointersareusedinsituationswherereferencescannotbeused,e.g.functions
returningallocatedmemoryorparent/childcollectionrelationshipswherecircular
dependencieswouldpreventtheuseofreferences.
C++11deprecates NULLinfavourofnewkeyword nullptrtosolveaproblemwith
functionoverloading.
Variables
72

voidread(Data*data);
voidread(intvalue);
//Whichfunctionarewecallinghere?
read(NULL);
Since NULLisessentially #defineNULL0and0isaninteger,wecallthewrongfunctionby
accident.SoC++introducesanexplicit nullptrforthispurpose.
read(nullptr);
InRust:
Rustsupportspointers,normallycalledrawpointershoweveryouwillrarelyusethemunless
youneedtointeractwithCAPIorsimilarpurposes.
ApointerlooksfairlysimilartothatofC++:
//Thisisareferencecoercedtoaconstpointer
letage:u16=27;
letage_ptr:*constu16=&age;
//Thisisamutreferencecoercedtoamutablepointer
letmuttotal:u32=0;
lettotal_ptr:*mutu32=&muttotal;
Althoughyoucanmakeapointeroutsideofanunsafeblock,manyofthefunctionsyou
mightwanttoperformonpointersareunsafebydefinitionandmustbeinside unsafe
blocks.
Thedocumentationinfullishere.
References
InC++
Areferenceisalsoavariablethatpointstoanaddressbutunlikeapointer,itcannotbe
reassignedanditcannotbe NULL.Thereforeareferenceisgenerallyassumedtobesafer
thanapointer.Itisstillpossiblefortheareferencetobecomedangling,assumingthe
addressitreferencedisnolongervalid.
Variables
73

InRust
Areferenceisalsolifetimetrackedbythecompiler.
Tuples
Atupleislistofvaluesheldinparenthesis.They'reusefulincaseswheretransientorad-hoc
dataisbeingpassedaroundandyoucannotbebotheredtowriteaspecialstructjustforthat
case.
InC++
C++doesnotnativelysupporttuples,butC++11providesatemplateforpassingthem
aroundlikeso:
#include<tuple>
std::tuple<int,int>get_last_mouse_click(){
returnstd::make_tuple(100,20);
}
std::tuple<int,int>xy=get_last_mouse_click();
intx=std::get<0>(xy);
inty=std::get<1>(xy);
InRust
Tuplesarepartofthelanguageandthereforethey'refarmoreterseandeasytoworkwith.
fnget_last_mouse_click()->(i32,i32){
(100,20)
}
//Either
let(x,y)=get_last_mouse_click();
println!("x={},y={}",x,y);
//or
letxy=get_last_mouse_click();
println!("x={},y={}",xy.0,xy.1);
Variables
74

Variables
75

Literals
C++
Integers
Integernumbersareadecimalvaluefollowedbyanoptionaltypesuffix.
InC++anintegerliteralcanbeexpressedasjustthenumberoralsowithasuffix.Valuesin
hexadecimal,octalandbinaryaredenotedwithaprefix:
//Integers
42
999U
43424234UL
-3456676L
329478923874927ULL
-80968098606968LL
//C++14
329'478'923'874'927ULL
//Hex,octal,binary
0xfffe8899bcde3728//or0X
07583752256
0b111111110010000//or0B
The u, l,and llsuffixesonintegersdenotesifitis unsigned, longora longlong
type.The uand l/llcanbeupperorlowercase.Ordinarilythe umustprecedethe
sizebutC++14allowsthereverseorder.
C++14alsoallowssinglequotestobeinsertedintothenumberasseparators-thesequotes
canappearanywhereandareignored.
Floatingpointnumbers
Floatingpointnumbersmayrepresentwholeorfractionalnumbers.
Booleanvalues
Literals
76

C/C++ boolliteralsare trueor false.
CharactersandStrings
Acharacterliteralisenclosedbysinglequotesandanoptionalwidthprefix.Theprefix L
indicatesawidecharacter, uforUTF-16and UforUTF-32.
'a'
L'a'//wchar_t
u'\u20AC'//char16_t
U'\U0001D11E'//char32_t
Oneoddityofa charliteralisthat sizeof('a')yields sizeof(int)inCbut sizeof(char)
inC++.Itisn'tagoodideatotestthesizeofacharacterliteral.
A char16_tand char32_taresufficienttoholdanyUTF-16andUTF-32codeunit
respectively.
Astringisasequenceofcharactersenclosedbydoublequotes.Azerovalueterminatoris
alwaysappendedtotheend.Prefixesworkthesameasforcharacterliteralswithan
additional u8typetoindicateaUTF-8encodedstring.
"Hello"
u8"Hello"//charwithUTF-8
L"Hello"//wchar_t
u"Hello"//char16_twithUTF-16
U"Hello"//char32_twithUTF-32
User-definedliterals
C++11introduceduser-definedliterals.Theseallowinteger,floats,charsandstringstohave
auserdefinedtypesuffixconsistingofanunderscoreandalowercasestring.Theprefix
mayactasaformofdecoratororevenaconstantexpressionoperatorwhichmodifiesthe
valueatcompiletime.
C++14goesfurtheranddefinesuser-definedliteralsforcomplexnumbersandunitsoftime.
Seethelinkformoreinformation.
Rust
Literals
77

Integers
InRustnumberliteralscanalsobeexpressedasjustthenumberoralsowithasuffix.
Valuesinhexadecimal,octalandbinaryarealsodenotedwithaprefix:
//Integers
123i32;
123u32;
123_444_474u32;
0usize;
//Hex,octal,binary
0xff_u8;
0o70_i16;
0b111_111_11001_0000_i32;
TheunderscoreinRustisaseparatorandfunctionsthesamewayasthesinglequotein
C++14.
Floatingpointnumbers
Floatingpointnumbersmayrepresentwholeorfractionalnumbers.Aswithintegersthey
maybesuffixedtoindicatetheirtype.
leta=100.0f64;
letb=0.134f64;
letc=2.3f32;//But2.f32isnotvalid(note1)
letd=12E+99_E64;
Onequirkwithfloatingpointnumbersisthedecimalpointisusedforfloatassignmentsbut
it'salsousedformemberandfunctioninvocation.Soyoucan'tsay 2.f32sinceitthinks
youarereferencingf32on2.Insteadsyntaxrequiresyoutosay 2.f32oralterhowyou
declarethetype,e.g. letv:f32=2.;.
Booleans
Booleanliteralsaresimply trueor false.
Literals
78

true
false
CharactersandStrings
AcharacterinRustisanyUTF-32codepointenclosedbysinglequotes.Thisvaluemaybe
escapedornotsince.rsfilesareUTF-8encoded.
Aspecialprefix bmaybeusedtodenoteabytestring,i.e.astringwhereeachcharacteris
asinglebyte.
'x'
'\''#Escapedsinglequote
b'&'#bytecharacterisau8
Stringsarethestringtextenclosedbydoublequotes:
"Thisisastring"
b"Thisisabytestring"
Theprefix bdenotesabytestring,i.e.singlebytecharacters.Rustallowsnewlines,space,
doublequotesandbackslashestobeescapedusingbackslashnotationsimilartoC++.
"Thisisa\
multilinestring"
"Thisstringhasanewline\nandanescaped\\backslash"
"Thisstringhasahexchar\x52"
Stringscanalsobe'raw'toavoidescaping.Inthiscase,thestringisprefixedrfollowedby
zeroormorehashmarks,thestringindoublequotesandthesamenumberofhashmarks
toclose.Bytestringsareuninterprettedbytevaluesinastring.
r##"Thisisarawstringthatcancontain\n,\andotherstuff
withoutescaping"##
br##"Arawbytestringwith"stuff"like\n\,\uandother
things"##
Literals
79

Literals
80

Collections
Acollectionissomethingthatholdszeroormoreelementsinsomefashionthatallowsyou
toenumeratethoseelements,addorremoveelements,findthemandsoforth.
Vector-adynamicarray.Appendingorremovingelementsfromtheendischeap
(providingthearrayislargeenoughtoaccomodateanadditionalitem).Insertingitems
orremovingthemfromanyotherpartofthearrayismoreexpensiveandinvolves
memorymovements.Generallyspeakingyoushouldalwaysreserveenoughspaceina
vectorforthemostelementsyouanticipateitwillhold.Reallocatingmemorycanbe
expensiveandleadtofragmentation.
Vecdeque-aringbufferarray.Itemscanbeaddedorremovedfromeitherend
relativelycheaply.Itemsinthearrayarenotarrangedsequentiallysothereisalittle
morecomplexitytomanagingwraparoundandremovalthanaVector.
LinkedList-alinkedlistindividuallyallocatesmemoryforeachelementmakingitcheap
toaddorremoveelementsfromanywhereinthelist.Howeverthereisalotmore
overheadtoiteratingthelistbyindexandmuchmoreheapallocation.
Set-acollectionthatholdsauniquesetofitems.Insertingaduplicateitemwillnot
succeed.Somesetsmaintaintheorderofinsertion.Setsareusefulwhereyouwantto
weedoutduplicatesfromaninput.
Map-acollectionwhereeachitemisreferencedbyauniquekey.Somemapscan
maintaintheorderofinsertion.
C++andRusthavehavecollectionsaspartoftheirstandardlibraryasiscommonwith
modernlanguages.
C C++ Rust
-std::vector std::vec::Vecor std::collections::VecDeque
-std::list std::collections::LinkedList
-std::set std::collections::HashSet, std::collections::BTreeSet
-std::map std::collections::HashMap, std::collections::BTreeMap
Chasnostandardcollectionclassesortypes.SomelibrariesoffercollectionAPIssuchas
gliborcii.
Iterators
Collections
81

Iteratorsareareferencetoapositioninacollectionwiththemeanstostepthroughthe
collectiononeelementatatime.
C++
C++11providesashorthandwayofiteratingacollection:
std::vector<char>values;
for(constchar&c:values){
//dosomethingtoprocessthevalueinc
}
IteratorsaremoreexplicitinC++98andbeforeandthecodeinC++11isbasicallyequivalent
tothis:
std::vector<char>values;
for(std::vector<char>::const_iteratori=values.begin();i!=
values.end();++i){
constchar&c=*i;
//dosomethingtoprocessthevalueinc
}
Thisisquiteverbose,butessentiallyeachcollectiontypedefinesamutable iteratorand
immutable const_iteratortypeandcalling beginreturnsaniteratortothebeginningof
thecollection.Callingthe ++operatoroverloadontheiteratorcausesittoadvancetothe
nextelementinthecollection.Whenithitstheexclusivevaluereturnedby endithas
reachedtheendofthecollection.
Obviouslywithanindexedtypesuchasa vectoryoucouldalsoreferenceelementsby
index,butitisfarmoreefficienttouseiteratorsforothercollectiontypes.
Processingcollections
C++providesanumberofutilitytemplatesinformodifyingsequencesincollectionsonthe
fly.
Rust
Collections
82

RustalsohasiteratorswhichworkinasimilarfashiontoC++-incrementingtheirway
throughcollections.
TODO
TODOchainingiteratorstogether
TODOmappingonecollectiontoanothercollection
Collections
83

Structs
C++
A classanda structinC++arelargelythesamethingfromanimplementation
standpoint.Theybothholdfieldsandtheybothcanhavemethodsattachedtotheclass
(static)orinstancelevel.
classFoo{
public:
//Methodsandmembersherearepubliclyvisible
doublecalculateResult();
protected:
//Elementshereareonlyvisibletoourselvesandsubclasses
virtualdoubledoOperation(doublelhs,doublerhs);
private:
//Elementshereareonlyvisibletoourselves
booldebug_;
};
Thedefaultaccesslevelis publicforstructand privateforclass.Somerulesabout
templatesonlyapplytoclasses.
Fromapsychologicalperspecta structtendstobeusedtoholdpublicdatathatislargely
staticand/orpassedaround.A classtendstobesomethingmoreselfcontainedwith
methodsthatarecalledtoaccessormanageprivatefields.
Sotheseareequivalents:
Structs
84

structFoo{//asastruct
private:
};
classFoo{//Asaclass
};
//Ortheotherwayaround
structBar{
};
classBar{
public:
};
Classescanalsouseanaccessspecifiertoinheritfromabaseclass.Soaclassmay
specify public, protectedor privatewhenderivingfromanotherclassdependingon
whetheritwantsthosemethodstobevisibletocallers,orsubclasses.
Classesandstructsmayhavespecialconstructoranddestructormethodswhichare
describedinsectionsbelow.
classSize{
public:
Size(intwidth,intheight);
intwidth_;
intheight_;
intarea()const;
};
Theninthe.cppfileyoumightimplementtheconstructorandmethod:
Size::Size(intwidth,intheight):width_(width),
height_(height){}
intSize::area(){returnwidth_*height_;}
Structs
85

Rust
Rustonlyhasstructs.A structconsistsofadefinitionwhichspecifiesthefieldsandtheir
accesslevel(publicornot),andan implsectionwhichcontainstheimplementationof
functionsboundtothestruct.
structSize{
pubwidth:i32;
pubheight:i32;
}
An implsectionfollowscontainingtheassociatedfunctions:
implSize{
pubfnnew(width:i32,height:i32)->Size{
Size{width:width,height:height,}
}
pubfnarea(&self)->i32{
self.width*self.height
}
}
The new()functionhereisaconveniencemethodthatreturnsastructpreinitialisedwith
theargumentssupplied.The area()functionspecifiesa &selfargumentandreturnsan
areacalculation.Anyfunctionthatsuppliesa &self,or &mutselfcanbecalledfromthe
variableboundtothestruct.
letsize=Size::new(10,20);
println!("Size={}",size.area());
The selfkeywordworksinmuchthesamewayasC++uses this,asareferencetothe
structfromwhichthefunctionwasinvoked.Ifafunctionmodifiesthestructitmustsay &mut
self,whichindicatesthefunctionmodifiesthestruct.
ThereisnoinheritanceinRust.Instead,a structmayimplementzeroormoretraits.Atrait
describessomekindofbehaviorthatcanbeassociatedwiththestructanddescribedfurther
lateroninthischapter.
Structs
86

Constructors
InC++allclasseshaveimplicitorexplicitconstructors.Eitherthecompilergeneratesthem
oryoudo,oramixofboth.
Animplicitdefaultconstructor,copyconstructorandassignmentoperatorwillbecreated
whenaclassdoesnotdefineitsown.Wesawonpage73whythiscouldbereallybad
news.
WhatbecomesobviousfromreadingthereisalotofnoiseandpotentialforerrorinC++.
Therewouldbeevenmoreifrawpointerswereusedinsteadofa std::unique_ptrhere.
InRust,thingsaresimpler,andwe'llseehowitshakesouterrors.
Firstoff,let'sdeclareourequivalentstructinRust:
structPerson{
pubname:String,
pubage:i32,
pubcredentials:Option<Credentials>,
}
Sincecredentialsareoptional,wewrapinan Optionobject,i.e.credentialsmightbeNone
oritmightbe Some(Credentials).Anycodeanywhereinthesystemcaninstantiatea
Personsimplybedeclaringaninstance:
letperson=Person{name:String::from("Bob"),age:20,
credentials:None}
InRustyoucannotcreateastructwithoutinitialisingallitsmemberssowecannothavea
situationwherewedon'tknowwhatisineachfield-itMUSTbesetbyourcode.
Butdeclaringthestructisabitclumsy,especiallyifthestructiscreatedinlotsofplaces.So
canwritefunctionthatbehaveslikeaconstructorinC++.
InsteadyouimplementastaticmethodintheimploftheStructwhichreturnsaninitialised
struct,e.g.
Structs
87

implPerson{
pubfnnew(name:String,age:String)->Person{
Person{name:name.clone(),age:age,credentials:None}
}
}
NotethatRustdoesnotsupportoverloads.Soifwehadmultiple"constructor"methods,
theywouldeachhavetohaveuniquenames.
Finallywhatiswewantedtocopythe Personstruct?
BydefaultRustdoesnotallowcopyingonuser-definedstructs.Assigningavariableto
anothervariablemovesownership,itdoesn'tcopy.
Therearetwowaystomakeauser-definedstructcopyable
1. implementthe Copytraitwhichmeansassignmentisimplicit,butiswhatwewant?Do
wereallywanttomakecopiesofastructbyaccident?
2. implement Cloneinsteadtoadda clone()methodandrequireanexplicitcallto
clone()ordertoduplicatethestructacopy.
Butthecompilercanderiveclone()providingallthemembersofthestructimplementthe
Clonetrait.
Structs
88

#[derive(Clone)]
structPerson{
pubname:String,
pubage:i32,
pubcredentials:Option<Credentials>,//Credentialsmust
implementClone
}
implPerson{
pubfnnew(name:String,age:String)->Person{
Person{name:name.clone(),age:age,credentials:None}
}
}
//...
letp=Person::new(String::from("Michael"),20);
letp2=p.clone();
WhatwecanseeisthatRust'sconstructionand clone()behaviorisbasicallydeclarative.
WesawhowC++hasallkindsofrulesandnuancestoconstruction,copyconstructionand
assignmentwhichmakeitcomplicatedandpronetoerror.
Destructors
AC++destructorisaspecializedmethodcalledwhenyourobjectgoesoutofscopeoris
deleted.
classMyClass{
public:
MyClass():someMember_(newResource()){}
~MyClass(){
deletesomeMember_;
}
private:
Resource*someMember_;
}
Structs
89

InC++youcandeclareaclassdestructortobecalledwhentheobjectisabouttobe
destroyed.Youhavetouseavirtualdestructorifyourclassinheritsfromanotherclassin
caseacallercalls deleteonthebaseclass.
SinceRustdoesnotdoinheritanceanddoesnothaveconstructors,themannerinwhich
youcleanupisdifferentandsimpler.Insteadofadestructoryouimplementthe Droptrait.
implDropforShape{
fndrop(&mutself){
println!("Shapedropping!");
}
}
Thecompilerrecognizesthistrait.Ifyouimplementthistraitthenthecompilerknowstocall
your drop()functionpriortodestroyingyourstruct.It’sthatsimple.
Occasionallytheremightbeareasontoexplicitlydropastructbeforeitgoesoutofscope.
Perhapstheresourcesheldbythevariableshouldbefreedassoonaspossibletoreleasea
resourcewhichisincontention.Whateverthereason,theansweristocall droplikethis:
{
letsome_object=SomeObject::new();
//...
//Ordinarilysome_objectmightgetdestroyedlater,
//butthismakesitexplicitlyhappenhere
drop(some_object);
//...
}
Accessspecifierrules
AC++classcanhideorshowmethodsandmemberstoanyotherclass,ortothingsthat
inheritfromitselfusingthepublic,privateandprotectedkeywords:
public–canbeseenbyanycodeinternalorexternaltotheclass
private–canonlybeusedbycodeinternaltotheclass.Notevensubclassescan
accessthesemembers
protected–canbeusedbycodeinternaltotheclassandbysubclasses.
Structs
90

Aclassmaydesignateanotherfunctionorclassasafriendwhichhasaccesstotheprivate
andprotectedmembersofaclass.
Rustmakesthingssomewhatsimpler.
Ifyouwantastructtobevisibleoutsideyourmoduleyoumarkit pub.Ifyoudonotmarkit
pubthenitisonlyvisiblewithinthemoduleandsubmodules.
pubstructPerson{/*...*/}
Ifyouwantpublicaccessamemberofastruct(includingmodifyingitifitsmutable),then
markit pub.
pubstructPerson{
pubage:u16,
}
Ifyouwantsomethingtobeabletocallafunctiononyourstructyoumarkit pub.
implPerson{
pubfnis_adult(&self)->bool{
self.age>=18
}
}
Functions
Functionscanbeboundtoastructwithinan implblock:
Structs
91

implShape{
pubfnnew(width:u32,height:u32)->Shape{
Shape{width,height}
}
pubfnarea(&self)->i32{
self.width*self.height
}
pubfnset(&mutself,width:i32,height:i32){
self.width=width;
self.height=height;
}
}
Functionsthatstartwitha &self/ &mutselfparameterareboundtoinstances.Those
withoutareboundtothetype.Sothe new()functioncanbecalledas Shape::new().
Where &selfisprovided,thefunctionisinvokedontheinstance.Soforexample:
letshape=Shape::new(100,100);
letarea=shape.area();
Where &mutselfisprovideditsignifiesthatthefunctionmutatesthestruct.
UnlikeC++,allaccesstothestructhastobequalified.InC++youdon'tpublishing_interval:
Double,lifetime_count:UInt32,max_keep_alive_count:UInt32,
max_notifications_per_publish:UInt32,priority:Bytehavetosay this->foo()tocallfoo()
fromanothermemberoftheclass.Rustrequirescodetosayunambiguously self.foo().
Staticfunctions
Staticfunctionsaremerelyfunctionsinthe implblockthatdonothave &selfor &mut
selfastheirfirstparameter,e.g.
Structs
92

implCircle{
fnpi()->f64{std::f64::consts:PI}
}
//...
letpi=Circle::pi();
Inotherwordsthey'renotboundtoaninstanceofatype,buttothetypeitself.Forexample,
Circle::pi().
Traits
C++allowsoneclasstoinheritfromanother.Generallythisisausefulfeaturealthoughit
cangetprettycomplexifyouimplementmultipleinheritance,particularlythedreaded
diamondpattern.
Aswe’vefoundout,Rustdoesn’thaveclassesatall–they’restructswithboundfunctions.
Sohowdoyouinheritcode?Theanswerisyoudon’t.
Insteadyourstructmayimplementtraitswhichareabitlikepartialclasses.
Atraitisdeclaredlikeso:
traitHasCircumference{
fncircumference(&self)->f64;
}
Herethetrait HasCircumferencehasafunctioncalled circumference()whosesignatureis
definedbutmustbeimplemented.
Atypecanimplementthetraitbydeclaringand implofit.
implHasCircumferenceforSize{
fncircumference(&self)->i32{
2.0*std::f64::consts::PI*self.radius
}
}
Atraitmaysupplydefaultfunctionimplementations.Forexample,a HasDimensionstrait
mightimplement area()tosparetheimplementorthebotherofdoingit.
Structs
93

traitHasDimensions{
fnwidth(&self)->u32;
fnheight(&self)->u32;
fnarea(&self)->u32{
self.width()*self.height()
}
}
Lifetimes
InC++anobjectlivesfromthemomentitisconstructedtothemomentitisdestructed.
Thatlifetimeisimplicitifyoudeclaretheobjectonthestack.Theobjectwillbecreated/
destroyedasitgoesinandoutofscope.Itisalsoimplicitifyourobjectisamemberof
anotherobject-thelifetimeiswithinthecontainingobject,andthedeclarationorderofother
membersinthecontainingobject.
However,ifyouallocateyourobjectvia newthenitisuptoyouwhento delete.Ifyou
deletetoosoon,orforgetto deletethenyoumaydestabilizeyourprogram.C++
encouragesusingsmartpointersthatmanagethelifetimeofyourobject,tyingittothe
implicitlifetimeofthesmartpointeritself-whenthesmartpointerisdestroyed,itdeletesthe
heldpointer.Amoresophisticatedkindofsmartpointerallowsmultipleinstancesofthe
samepointertoexistatonce,andreferencecountingisusedsothatwhenthelastsmart
pointerisdestroyed,itdestroyesthepointer.
Evenso,C++itselfwillnotcareifyouinitializedaclasswithareferenceorpointerto
somethingthatnolongerlives.Ifyoudothis,yourprogramwillcrash.
Let'swritean Incrementorclasswhichincrementsanintegervalueandreturnsthatvalue.
classIncrementor{
public:
Incrementor(int&value):value_(value){}
intincrement(){return++value_;}
private:
int&value_;
};
Structs
94

Thisseemsfine,butwhatifweuseitlikethis?
IncrementormakeIncrementor(){
//Thisisabadidea
intvalue=5;
returnIncrementor(value);
}
Thiscodepassesareferencetoan intintotheclassconstructorandreturnsthe
Incrementorfromthefunctionitself.Butwhen increment()iscalledthereferenceis
danglingandanythingcanhappen.
Rustlifetimes
Rustdoescareaboutthelifetimeofobjectsandtracksthemtoensurethatyoucannot
referencesomethingthatnolongerexists.Mostofthetimethisisautomaticandself-evident
fromtheerrormessageyougetifyoutrysomethingbad.
Thecompileralsoimplementsaborrowcheckerwhichtracksreferencestoobjectsto
ensurethat:
1. Referencesareheldnolongerthanthelifetimeoftheobjecttheyreferto.
2. Onlyasinglemutablereferenceispossibleatatimeandnotconcurrentlywith
immutablereferences.Thisistopreventdataraces.
Thecompilerwillgeneratecompileerrorsifitfindscodeinviolationofitsrules.
Solet'swritetheequivalentof IncrementorabovebutinRust.TheRustcodewillholda
referencetoainteger i32andincrementitfromaboundfunction:
structIncrementor{
value:&muti32
}
implIncrementor{
pubfnincrement(&mutself)->i32{
*self.value+=1;
*self.value
}
}
Structs
95

Seemsfine,butthefirsterrorwegetis:
2|value:&mutu32
|^expectedlifetimeparameter
Wetriedtocreateastructthatmanagesareference,butthecompilerdoesn'tknowanything
aboutthisreference'slifetimeandsoithasgeneratedacompileerror.
Tohelpthecompilerovercomeitsproblem,wewillannotateourstructwithalifetimewhich
wewillcall 'a.Thelabelisanythingyoulikebuttypicallyit'llbealetter.
Thislifetimelabelisahintonourstructthatsaysthereferenceweuseinsidethestructmust
havealifetimeofatleastasmuchthestructitself-namelythat Incrementor<'a>and
value:&'amuti32sharethesamelifetimeconstraintandthecompilerwillenforceit.
structIncrementor<'a>{
value:&'amuti32
}
impl<'a>Incrementor<'a>{
pubfnincrement(&mutself)->i32{
*self.value+=1;
*self.value
}
}
Withtheannotationinplace,wecannowusethecode:
letmutvalue=20;
letmuti=Incrementor{value:&mutvalue};
println!("value={}",i.increment());
Notethattheannotation 'acouldbeanylabelwelike- 'incrementwouldworkifwe
wanted,butobviouslyitsmorelongwinded.
Thereisaspeciallifetimecalled 'staticthatreferstothingslikestaticstringsand
functionswhichhavealifetimeaslongastheruntimeandmaythereforebeassumedto
alwaysexist.
Lifetimeelision
Structs
96

Rustallowsreferencelifetimestobeelided(afancywordforomit)inmostfunction
signatures.
Basically,itassumesthatwhenpassingareferenceintoafunction,thatthelifetimeofthe
referenceisimplicitlylongerthanthefunctionitselfsotheneedtoannotateisnotnecessary.
fnfind_person(name:&str)->Option<Person>
//insteadof
fnfind_person<'a>(name:&'astr)->Option<Person>
Therulesforelisionaredescribedinthefurtherreferencelink.
Furtherreference
Lifetimesarealargesubjectandthedocumentationishere.
Structs
97

Comments
RustcommentsaresimilartoC++excepttheymaycontainUnicodebecause.rsfilesare
UTF-8encoded:
/*
Thisisacomment
*/
//ThisacommentwithUnicode,你好
Butinadditionanythingthatusestripleslash ///annotationcanbeparsedbyatoolcalled
rustdoctoproducedocumentation:
///Thisisacommentthatbecomesdocumentationfordo_thing
below
pubfndo_thing(){}
///Returnedbyserveriftheresourcecouldnotbefound
pubconstNOT_FOUND=404;
Runnining cargodoconaprojectwillcauseHTMLdocumentationtobeproducedfrom
annotatedcommentswithinthefile.
AnnotationiswritteninMarkdownformat.Thatmeansyouhaveahumanreadablelanguage
forwritingrich-textdocumentationandifit'snotenoughyoucanresorttoHTMLviatags.
Seehereforfulldocumentation
Comments
98

Lifetimes,ReferencesandBorrowing
WhenyouassignanobjecttoavariableinRust,youaresaidtobebindingit.i.eyour
variable"owns"theobjectforaslongasitisinscopeandwhenitgoesoutofscopeitis
destroyed.
{
letv1=vec![1,2,3,4];//Veciscreated
...
}//v1goesoutofscope,Vecisdropped
Sovariablesarescopedandthescopeistheconstraintthataffectstheirlifetime.Outsideof
thescope,thevariableisinvalid.
Inthisexample,itisimportanttorememberthe Vecisonthestackbutthepointerit
allocatestoholditselementsisontheheap.Theheapspacewillalsoberecoveredwhen
the Vecisdropped.
Ifweassignv1toanothervariable,thenalltheobjectownershipismovedtothatother
variable:
{
letv1=vec![1,2,3,4];
letv2=v1;
...
println!("v1={:?}",v1);//Error!
}
Thismayseemweirdbutit'sworthrememberingaseriousproblemwesawinC++,thatof
copyconstructorerrors.Itistooeasytoduplicateaclassandinadvertantlyshareprivate
dateorstateacrossmultipleinstances.
Wedon'twantobjectsv1andv2toshareinternalstateandinRusttheydon't.Rustmoves
thedatafromv1tov2andmarksv1asinvalid.Ifyouattempttoreferencev1anymorein
yourcode,itwillgenerateacompileerror.Thiscompileerrorwillindicatesthatownership
wasmovedtov2.
Likewise,ifwepassthevaluetoafunctionthenthatalsomovesownership:
Lifetimes,ReferencesandBorrowing
99

{
letv1=vec![1,2,3,4];
we_own_it(v1);
println!("v={:?}",v1);
}
fnwe_own_it(v:Vec<i32>){
//...
}
Whenwecallwe_own_it()wemovedownershipoftheobjecttothefunctionanditnever
cameback.Thereforethefollowingcallusingv1isinvalid.Wecouldcallavariationofthe
functioncalledwe_own_and_return_it()whichdoesreturnownership:
v1=we_own_and_return_it(v1)
...
fnwe_own_and_return_it(v:Vec<i32>)->Vec<i32>{
//...
v1
}
Butthat'sprettymessyandthereisabetterwaydescribedinthenextsectioncalled
borrowing.
ThesemoveassignmentslookweirdbutitisRustprotectingyoufromthekindsofcopy
constructorerrorthatisalltoocommoninC++.Ifyouassignanon-Copyableobjectfrom
onevariabletoanotheryoumoveownershipandtheoldvariableisinvalid.
Ifyoutrulywanttocopytheobjectfromonevariabletoanothersothatbothhold
independentobjectsyoumustmakeyourobjectimplementtheCopytrait.Normallyit's
bettertoimplementtheClonetraitwhichworksinasimilarwaybutthroughanexplicit
clone()operation.
Variablesmustbeboundtosomething
Anotherpoint.Variablesmustbeboundtosomething.Youcannotuseavariableifithasn't
beeninitializedwithavalueofsomekind:
Lifetimes,ReferencesandBorrowing
100

letx:i32;
println!("Thevalueofxis{}",x);
ItisquitevalidinC++todeclarevariableanddonothingwithit.Orconditionallydo
somethingtothevariablewhichconfusesthecompilersoitonlygeneratesawarning.
intresult;
{
//Thescopeistocontrolthelifetimeofalock
lock_guard<mutex>guard(data_mutex);
result=do_something();
}
if(result==0){
debug("resultsucceeded");
}
TheRustcompilerwillthrowanerror,notawarning,ifvariablesareuninitialised.Itwillalso
warnyouifyoudeclareavariableandendupnotusingit.
ReferencesandBorrowing
We'veseenthatownershipofanobjectistrackedbythecompiler.Ifyouassignonevariable
toanother,ownershipoftheobjectissaidtohavemovedtotheassignee.Theoriginal
variableisinvalidandthecompilerwillgenerateerrorsifitisused.
Unfortunatelythisextendstopassingvaluesintofunctionsandthisisanuisance.But
variablebindingscanbeborrowed.Ifyouwishtoloanavariabletoafunctionforits
duration,youcanpassareferencetotheobject:
Lifetimes,ReferencesandBorrowing
101

{
letmutv=Vec::new();//emptyvector
fill_vector(&mutv);
//...
println!("Vectorcontains{:?}",v);
}
//...
fnfill_vector(v:&mutVec<i32>){
v.push(1);
v.push(2);
v.push(3);
}
Herewecreateanemptyvectorandpassamutablereferencetoittoafunctioncalled
fill_vector().Thecompilerknowsthatthefunctionisborrowingvandthenownershipis
returnedtovafterthefunctionreturns.
Lifetimes,ReferencesandBorrowing
102

Expressions
Anexpressionissomethingthatevaluatestosomething.JustlikeC++moreorless...
letx=5+5;//expressionevaluatesto10
Butblocksareexpressionstoo
Whereitgetsmoreinterestingisthatablockofcode,denotedbycurlybracesalso
evaluatestoanexpression.Thisislegalcode:
letx={};
println!("x={:?}",x);
Whatwasassignedtox?Inthiscasetheblockwasemptysoxwasassignedwiththevalue
of ().Thevalue ()isaspecialunitarytypethatessentiallymeansneitheryesorno.It
justmeans"value".Thatisthedefaulttypeofanyfunctionortype.Itworksalittlelike void
inC++meaningthevalueismeaninglesssodon'tevenlookatit.
x=()
Thisblockalsoreturnsavalueof ().
letx={println!("Hello");};
println!("x={:?}",x);
Again,that'sbecausealthoughtheblockdoesstuff(printHello),itdoesn'tevaluateto
anythingsothecompilerreturns ()forus.
Sofarsouseless.Butwecanchangewhattheblockexpressionevaluatesto:
Expressions
103

letx={
letpi=3.141592735;
letr=5.0;
2.0*pi*r
};
println!("x={}",x);
Nowxassignedwiththeresultofthelastlinewhichisanexpression.Notehowthelineis
notterminatedwithasemicolon.Thatbecomestheresultoftheblockexpression.Ifwe’dput
asemicolonontheendofthatlineaswedidwiththeprintln!("Hello"),theexpressionwould
evaluateto().
Useinfunctions
Trivialfunctionscanjustomitthereturnstatement:
pubfnadd_values(x:i32,y:i32)->i32{
x+y
}
Youcanusereturninblockstoo
Sometimesyoumightexplicitlyneedtousethereturnstatement.Theblockexpression
evaluatesattheendoftheblocksoifyouneedtobailearlyyoucouldjustusereturn.
pubfnfind(value:&str)->i32{
ifvalue.len()==0{
return-1;
}
database.do_find(value)
}
Simplifyingswitchstatements
InCorC++you'lloftenseecodelikethis:
Expressions
104

std::stringresult;
switch(server_state){
caseWAITING:
result="Waiting";
break;
caseRUNNING:
result="Running";
break;
caseSTOPPED:
result="Stopped";
break;
}
}
Thecodewantstotestavalueinserver_stateandassignastringtoresult.Asidefrom
lookingabitclunkyitintroducesthepossibilityoferrorsincewemightforgettoassign,or
addabreak,oromitoneofthevalues.
InRustwecanassigndirectlyintoresultoffromamatchbecauseeachmatchconditionisa
blockexpression.
letresult=matchserver_state{
ServerState::WAITING=>"Waiting",
ServerState::RUNNING=>"Running",
ServerState::STOPPED=>"Stopped",
};
Notonlyisthishalfthelengthitreducesthescopeforerror.Thecompilerwillassignthe
blockexpression'svaluetothevariableresult.Itwillalsocheckthateachmatchblock
returnsthesamekindoftype(soyoucan'treturnafloatfromonematchandstringsfrom
others).ItwillalsogenerateanerroriftheServerStateenumhadothervaluesthatour
matchdidn'thandle.
Ternaryoperator
TheternaryoperatorinC/C++isanabbreviatedwaytoperformanif/elseexpression
condition,usuallytoassigntheresulttoavariable.
boolx=(y/2)==4?true:false;
Expressions
105

Rusthasnosuchequivalenttoaternaryoperatorbutitcanbeaccomplishedusingblock
expressions.
letx=ify/2==4{true}else{false};
UnlikeC/C++youcouldaddadditionaelseifs,matchesoranythingelsetothatproviding
eachbranchreturnsthesametype.
Expressions
106

Conditions
ConditionalcodeissimilarbetweenC++andRust.Youtestthebooleantruthofan
expressionandyoucanusebooleanoperatorssuchas&&and||tojoinexpressions
together.
intx=0;
while(x<10){
x++;
}
inty=10;
booldoCompare=true;
if(doCompare&&x==y){
printf("Theymatch!\n");
}
InRust:
letmutx=0;
whilex<10{
x=x+1;
}
lety=10;
letdo_compare=true;
ifdo_compare&&x==y{
println!("Theymatch!");
}
ThemostnotabledifferenceisthatRustomitstheouterbracessothecodeisslightly
cleaner.Youdon'thavetoomittheouterbracesbutthecompilerwillissueawarningifyou
leavethemin.
Ternaryoperator
Theternaryoperatoristhatspecial?:shorthandnotationyoucanusetoinC++forsimple
conditionals.
Conditions
107

intx=(y>200)?10:0;
Rustdoesnotsupportthisnotation,howeveryoumaytakeadvantageofhowablock
evaluatesasanexpressiontosaythisinstead:
letx=ify>200{10}else{0};
Sobasicallyyoucandoonelineconditionalassignmentsusingifandelse.Alsonotethat
youcouldeventhrowinan"elseif"ortwoifthat'swhatyouwantedtodo:
letc=get_temperature();
letwater_is=if(c>=100){"gas"}elseif(c<0){"solid"
}else{"liquid"};
Conditional"iflet"
Oneunusualfeatureisthe"iflet"pattern.Thiscombinesatesttoseeifsomethingmatches
apatternandifitdoes,toautomaticallyassigntheresulttothetuple.Itwouldbemost
commonlyseeincodethatreturnsanenumsuchasa Resultor Option.
Forexample:
fnsearch(name:&str)->Option<Person>{/*...*/}
//...
ifletSome(person)=search("fred"){
println!("Youfouldaperson{}",person);
}
else{
println!("Couldnotfindperson");
}
Conditions
108

Switch/Match
C++
A switchstatementinCorC++allowsaconditionoravariabletobecomparedtoaseries
ofvaluesandforcodeassociatedwiththosevaluestoexecutedasaresult.Thereisalsoa
defaultclausetomatchanyvaluethatisisnotcaughtexplicitly.
intresult=http_get();
switch(result){
case200:
success=true;
break;
case404:
log_error(result);
//Dropthrough
default:
success=false;
break;
}
Switchstatementscanbeasourceoferrorbecausebehaviourisundefinedwhena
defaultclauseisnotsupplied.Itisalsopossibletoinadvertentlyforgetthe break
statement.Intheaboveexample,thecodeexplicitly"drops"fromthe404handlerintothe
defaulthandler.Thiscodewouldworkfineprovidingsomeonedidn'tinsertsomeextra
clausesbetween404anddefault...
Additionallyswitchstatementsonlyworkonnumericvalues(or bool).
Rust
Matchislikea switchstatementonsteroids.
InC++aswitchisastraightcomparisonofanintegervalueofsomekind(includingchars
andenums),againstalistofvalues.Ifthecomparisonmatches,thecodenexttoitexecutes
untilthebottomoftheswitchstatementorabreakisreached.
TODO
Switch/Match
109

Switch/Match
110

Casting
Castingistheactofcoercingonetypetobeanother,ordynamicallyproducingthe
equivalentvalueintheothertype.
C++hasarangeofcastoperatorsthatturnapointerorvalueofonekindintoapointeror
valueofanotherkind.
const_cast<T>(value)-removestheconstenforcementfromavaluesoitmaybe
modified.
static_cast<T>(value)-attemptstoconvertbetweentypesusingimplicitanduser
definedconversions.
reinterpret_cast<T>(value)-acompilerdirectivetojusttreattheinputassomeother
kind.Itdoesnotinvolveanyformofconversion.
dynamic_cast<T>(value)-attemptstoconvertaclasspointer/referenceto/fromother
classesinitsinheritancehierarchy.Involvesruntimechecks.
TraditionalC-stylecast-aC++compilerwillattempttointerpretitasa const_cast,a
static_castanda reinterpret_castinvaryingcombinations.
That'saverybriefsummaryofcastingwhichprobablyinvokesmorequestionsthanit
answers.CastinginC++isverycomplexandnuanced.Somecastsmerelyinstructthe
compilertoignoreconstortreatonetypeasanother.Astaticcastmightinvolvecode
generationtoconvertatype.Adynamiccastmightaddruntimechecksandthrow
exceptions.
Rusthasnothingequivalenttothiscomplexity.Anumerictypemaybeconvertedtoanother
numerictypeusingthe askeyword.
leta=123i32;
letb=aasusize;
Anythingbeyondthisrequiresimplementingthe Into<>or From<>traitsandmaking
conversionanexplicitaction.
Thecompileralsodoesnotallowcodetocastaway const-nessortreatonetypeas
anotherexceptthrough unsafecodeblocks.
Transmutation
Casting
111

Enumerations
InC++an enumisabunchoflabelsassignedan intvalue.i.e.itisbasicallyabunchof
constantswithscalarvalues.
enumHttpResponse{
okay=200,
not_found=404,
internal_error=500,
};
C++11extendsthisconceptalittle,allowingyoutodeclarean enumthatusesanotherkind
ofintegraltype,e.g.a chartoholdthevalues.
enumLibraryCode:char{
checked_in='I',
checked_out='O',
checked_out_late='L'
};
InRustan enumcanbeascalarvaluejustlikeinC++.
enumHttpResponse{
Ok=200,
NotFound=404,
InternalError=500
};
Butanenumcanalsoholdactualdatasoyoucanconveyfarmoreinformationthanastatic
valuecouldbyitself.
enumHttpResponse{
Ok,
NotFound(String),
InternalError(String,String,Vec<u8>)
}
Enumerations
113

Youcanalsobindfunctionstotheenum:
implHttpResponse{
pubfncode(&self)=>{
match*self{
HttpResponse::Ok=>200,
HttpResponse::NotFound(_)=>404,
HttpResponse::InternalError(_,_,_)=>500,
}
}
}
Sowemighthaveafunctionthatmakesanhttprequestandreturnsaresponse:
fndo_request(url:&str)->HttpResponse{
ifurl=="/invalid"{
HttpResponse::NotFound(url.to_string())
}
else{
HttpResponse::Ok
}
}
//...
letresult=do_request("/invalid");
ifletHttpResponse::NotFound(url)=result{
println!("Theurl{}couldnotbefound",url);
}
Nowourcodeisabletoreturnamoremeaningfulresponseinanenumandthecodeisable
toextractthatresponsetoprintoutusefulinformation.
Enumerations
114

Loops
C++
Forloops
A forloopinC/C++consistsof3expressionsectionshousedinthe for()sectionanda
blockofcodetoexecute:
Thethreesegmentsofaforstatementallow:
Zeroormorevariablestobeinitialized(canbeempty)
Zeroormoreconditionstobetrueforthelooptocontinue(canbeempty)
Zeroormoreactionstoperformoneachiteration(canbeempty).
Sothisisavalidforloop:
//Infinite
for(;;){
//...
}
Soisthis:
for(inti=10,j=0;(j=i*i)<=100;i--){
//...
}
Thisisclearlyaconvolutedandsomewhatconfusingloopbecauseitmixesassignmentand
conditionaltestsintotheterminatingtext,butitisonewhichisentirelylegal.
Iteratingarange
AC++loopconsistsofaninitialisingexpression,aconditionexpressionandaaloop
expressionseparatedbysemicolons.Soaloopthatiteratesfrom0to100lookslikethis:
Loops
115

for(inti=0;i<100;i++){
cout<<"Number"<<i<<endl;
}
IteratingC++collections
C++introducestheconceptofiteratorstoitscollectionclasses.An iteratorissomething
thatcanincrementordecrementtotraverseacollection.
Sotoiterateacollectionfromoneendtotheother,aniteratorisassignedwiththe
collection's begin()iteratorandincrementeduntilitmatchesthe end()iterator.
for(std::vector<string>::const_iteratori=my_list.begin();i
!=my_list.end();++i){
cout<<"Value="<<*i<<end;
}
C++11providesnewrangebasedfor-loopwithsimplersyntaxwheniteratingoverarrays
andcollections:
std::vectorvalues;
...
for(constauto&v:values){
...
}
intx[5]={1,2,3,4,5};
for(inty:x){
...
}
InfiniteLoop
Aninfiniteloopisonethatneverends.ThetypicalwaytodothisinC++istotestagainstan
expressionthatalwaysevaluatestotrueoruseanemptyforloop:
Loops
116

while(true){
poll();
do_work();
}
//Orwithaforloop
for(;;){
poll();
do_work();
}
WhileLoop
C++hasconditional while(){}and do{}while()forms.Theformerteststhe
expressionbeforeitevenrunswhilethelatterrunsatleastoncebeforetestingthe
expression.
while(!end){
std::stringnext=getLine();
end=next=="END";
}
Thedo-whileforminC++willexecutetheloopbodyatleastoncebecausetheconditionis
onlytestedaftereachiterationinsteadofbefore.
inti=0;
do{
i=rand();
}while(i<20);
BreakandContinue
Ifyouneedtoexitalooporstartthenextiterationearlythenyouusethe breakand
continuekeywords.Thebreakkeywordterminatestheloop,thecontinue,causestheloop
toproceedtothenextiteration.
Loops
117

boolfoundAdministrator=false;
for(inti=0;i<loginCredentials;++i){
constLoginCredentialscredentials=fetchLoginAt(i);
if(credentials.disabled){
//Thisuserloginisdisabledsoskipit
continue;
}
if(credentials.isAdmin){
//Thisuserisanadministratorsononeedtosearchrest
oflist
foundAdministrator=true;
break;
}
//...
}
Rust
Forloop
Rust's forloopisactuallysugaroverthetopofiterators.Ifastructuredtypeimplements
thetrait IntoIteratoritcanbeloopedoverusinga forloop.
Basicallyinpseudocode,theloopdesugarstothis:
Ifstructuretypecanbeturned`IntoIterator`
Loop
IfletSome(item)=iterator.next(){
do_action_to_item(item)
Else
break;
End
Else
CompileError
Done
Iteratingarange
Loops
118

A RangeobjectinRustisexpressedas from..towhere fromand toarevaluesor
expressionsthatevaluatetovalues.
Forexample:
letrange=0..33;
//Variables
letmin=0;
letmax=100;
letrange2=min..max;
Arangeisinclusive/exclusive,i.e.theminimumvalueisincludedinthe Rangebutthe
maximumvalueisexclusive.
Hereisasimpleloopthatcountsfrom0to9
foriin0..10{
println!("Number{}",i);
}
Thevalue 0..10isa Rangethatrunsfrom0toexclusiveof10.Arangeimplementsthe
Iteratortraitsotheforloopadvancesoneelementatatimeuntilitreachestheend.
Iteratorshavealotoffunctionsonthemfordoingfancystuff,butonewhichisusefulinloops
isthe enumerate()function.Thistransformstheiteratorintoreturningatuplecontainingthe
indexandthevalueinsteadofjustthevalue.
Soforexample:
for(i,x)in(30..50).enumerate(){
println!("Index{}isvalue{}",i,x);
}
Forloop-Iteratingarraysandcollections
Hereisaloopthatiteratesanarray:
Loops
119

letvalues=[2,4,6,7,8,11,33,111];
forvin&values{
println!("v={}",v);
}
Noteyoucanonlyiterateoveranarraybyreferencebecauseiteratingitbyvaluewouldbe
destructive.
Wecandirectlyusethe iter()functionthatarraysandcollectionsimplementwhichworks
byreference:
letvalues=vec![2,4,6,7,8,11,33,111];
forvinvalues.iter(){
println!("v={}",v);
}
Ifthecollectionisamap,theniteratorswillreturnakeyandvaluetuple
usestd::collections::HashMap;
letmutvalues=HashMap::new();
values.insert("hello","world");
//...
for(k,v)in&values{
println!("key={},value={}",k,v);
}
Anotherwaytoiterateisusingthe for_each()functionontheiteratoritself:
letvalues=[2,4,6,7,8,11,33,111];
values.iter().for_each(|v|println!("v={}",v));
BreakandContinue
Rustalsohas breakand continuekeywordsandtheyoperateinasimilarfashion-they
operateontheinnermostloop.A continuewillstartonthenextiterationwhilea breakwill
terminatetheloop.
Loops
120

letvalues=vec![2,4,6,7,8,11,33,111];
forvin&values{
if*v%2==0{
continue;
}
if*v>20{
break;
}
println!("v={}",v);
}
Labels
The breakand continueworkbydefaultonthecurrentloop.Therewillbeoccasions
whereyouintendtobreakoutofanenclosingloopinstead.Forthoseoccasionsyoucan
labelyourloopsandpassthatlabelintothe breakor`continue:
'x:forxin0..10{
'y:foryin0..10{
ifx==5&&y==5{
break'x;
}
println!("x={},y={}",x,y);
}
}
InfiniteLoop
Rusthasanexplicitinfinite loopthatrunsindefinitely:
loop{
poll();
do_work();
}
Rustrecommendsusingthisformwhenaninfiniteloopisrequiredtoassistwithcode
generation.Notethataninfiniteloopcanstillbebrokenoutofusinga breakstatement.
Loops
121

WhileLoop
A whileloopinRustlooksprettysimilartoonewritteninC/C++.Themaindifferenceis
thatparenthesesarenotnecessaryaroundtheconditionaltest.
whilerequest_count<1024{
process_request();
request_count=request_count+1;
}
Rusthasnoequivalenttothedo-whileloopform.Itcanbesimulatedbutitlooksabit
inelegant:
letmuti=0;
loop{
i=i+1;
ifi>=20{break;}
}
Whileletloop
Justasthereisan ifletwhichtestsandassignsavaluethatmatchesapattern,thereis
alsoa whileletequivalent:
letmutiterator=vec.into_iter();
whileletSome(value)=iterator.next(){
process(value);
}
Thisloopwillbreakwhentheiteratorreturns None.
Loops
122

Functions
InC++thestandardformofafunctionisthis:
//Declaration
intfoo(boolparameter1,conststd::string¶meter2);
//Implementation
intfoo(boolparameter1,conststd::string¶meter2){
return1;
}
Usuallyyouwoulddeclarethefunction,eitherasaforwardreferenceinasourcefile,orina
header.Thenyouwouldimplementthefunctioninasourcefile.
Ifafunctiondoesnotreturnsomething,thereturntypeis void.Ifthefunctiondoesreturn
something,thenthereshouldbereturnstatementsforeachexitingbranchwithinthe
function.
Youcanforegothefunctiondeclarationintwosituations:
1. Ifthefunctionisinline,i.e.prefixedwiththe inlinekeyword.Inwhichcasethe
functioninitsentireityisdeclaredandimplementedinoneplace.
2. Ifthefunctionisnotinlinebutisdeclaredbeforethecodethatcallsitinthesame
sourcefile.Soiffunction fooabovewasonlyusedbyonesourcefile,thenjustputting
theimplementationintothesourcewouldalsoactasthedeclaration
InRusttheequivalentto fooaboveisthis:
fnfoo(parameter1:bool,parameter2:&str)->i32{
//implementation
1
}
Theimplementationisthedeclarationthereisnoseparationbetweenthetwo.Functionsthat
returnnothingomitthe ->returnsection.Thefunctioncanalsobedeclaredbeforeorafter
whatevercallsit.Bydefaultthefunctionisprivatetothemodel(andsubmodules)that
implementitbutmakingit pubfnexposesittoothermodules.
Functions
123

LikeC++,thefunctionmustevaluatetosomethingforeachexitingbranchbutthisis
mandatory.
Alsonote,thatthe returnkeywordisnotusuallyunecessary.Hereisafunctionthatadds
twovaluestogetherandreturnsthemwithnoreturn:
fnadd(x:i32,y:i32)->i32{
x+y
}
Whyisthereno return?AswesawinthesectiononExpressions,ablockevaluatestoa
valueifweomitthesemi-colonfromtheendso x+yistheresultofevaluatingthe
functionblockandbecomeswhatwereturn.
Thereareoccasionswereyouexplicitlyneedthereturnkeyword.Typicallyyoudothatifyou
wanttoexitthefunctionbeforeyougettotheendofthefunctionblock:
fnprocess_data(number_of_times:ui32)->ui32{
ifnumber_of_times==0{
return0;
}
letmutresult:ui32=0;
foriinnumber_of_times{
result+=i;
}
result
}
Variablearguments
C++functionscantakeavariablenumberofargumentswiththe...ellipsispattern.Thisis
usedinfunctionssuchasprint,scanfetc.
voidprintf_like(constchar*pattern,...);
Rustdoesnotsupportvariadicfunctions(thefancynameforthisability).Howeveryoucould
passadditionalargumentsinanarraysliceifthevaluesarethesame,orasadictionaryora
numberofotherways.
TODORustexampleofarrayslice
Functions
124

Anotheroptionistowriteyourcodeasamacro.Macroscantakeanynumberof
expressionssoyouareabletowritecodethattakesvariablearguments.Thisishowmacros
suchprintln!,format!andvec!work.
Defaultarguments
C++argumentscanhavedefaultvalues.
std::vector<Record>fetch_database_records(intnumber_to_fetch=
100);
Afunctiondefineswhatitsnameis,whattypesittakesandwhatvalue(ifany)itreturns.
Functionoverloading
C++functionscanbeoverloaded,e.g.
std::stringto_string(intx);
std::stringto_string(floatx);
std::stringto_string(boolx);
Rustdoesnotsupportoverloading.Asanalternative,eachvariationofthefunctionwould
havetobenameduniquely.
C++11alternativesyntax
C++11introducesanewsyntaxwhichisslightlyclosertoRust'sinstyle.
autofunction_name(typeparameter1,typeparameter2,...)->
return-type;
ThisformwascreatedtoallowC++functiondeclarationstomorecloselytoresemble
lambdafunctionsinsomescenariosandtohelpwithdecltypereturnvalues.
Functions
125

Polymorphism
C++
C++has4typesofpolymorphism:
1. Functionnameoverloading-multipledefinitionsofthesamefunctiontakingdifferent
arguments.
2. Coercion-implicittypeconversion,e.g.assigningadoubletoanintorabool.
3. Parametric-compiletypesubstitutionofparametersintemplates
4. Inclusion-subtypingaclasswithvirtualmethodsoverloadstheirfunctionality.Yourcode
canusethepointertoabaseclass,yetwhenyoucallthemethodyouarecallingthe
functionimplementedbythesubtype.
Thatistosay,thesamenamedfunctioncanbeoverloadedwithdifferentparameters.
Functionnameoverloading
classVariant{
public:
voidset();//Nullvariant
voidset(boolvalue);
voidset(intvalue);
voidset(floatvalue);
voidset(Array*value);
};
Oneofthebiggestissuesthatyoumightbegintoseefromtheaboveexampleisthatistoo
easytoinadvertantlycallthewrongfunctionbecauseC++willalsoimplicitlyconverttypes.
OntopofthatC++alsohasdefaultparametervaluesanddefaultconstructors.Soyoumight
callafunctionusingonesignatureandbecallingsomethingentirelydifferentafterthe
compilerresolvesit.
Polymorphism
126

//Samplecode
Variantv;
//...
v.set(NULL);
Thisexamplewillcalltheintegeroverloadbecause NULLevaluatesto0.Oneofthe
changesto C++11wastointroduceanexplicit nullptrvalueandtypetoavoidthisissues.
Rust
Rusthaslimitedsupportforpolymorphism.
1. Functionnameoverloading-thereisnone.Seesectionbelowforalternatives.
2. Coercion.Rustallowslimited,explictcoercionbetweennumerictypesusingthe as
keyword.Otherwiseseebelowforuseon Intoand Fromtraits.
3. Parameteric-similartoC++viagenerics
4. Inclusion-thereisnoinheritanceinRust.Thenearestthingtoavirtualmethodinrustis
atraitwithanimplementedfunctionthatanimplementationoverrideswithitsown.
Howeverthisoverrideisatcompiletime.
Alternativestofunctionnameoverloading
Ifyouhaveafewfunctionsyoucanjustdisambiguatethem,e.g.
fnnew(name:&str)->Foo{/*...*/}
fnnew_age(name:&str,age:u16)->Foo{/*...*/}
Usetraits
Acommonwaytodopolymorphismiswithtraits.
Therearetwostandardtraitsforthispurpose:
The From<>traitconvertsfromsometypeintotheourtype.
The Into<>traitconvertssometype(consumingitintheprocess)intoourtype
Youonlyneedtoimplement Fromor Intobecauseoneimpliestheother.
The Fromtraitiseasiertoimplement:
Polymorphism
127

usestd::convert::From;
implFrom<&'staticstr>forFoo{
fnfrom(v:&'staticstr)->Self{
Foo{/*...*/}
}
}
implFrom<(&'staticstr,u16)>forFoo{
fnfrom(v:(&'staticstr,u16))->Self{
Foo{/*...*/}
}
}
//...
letf=Foo::from("Bob");
letf=Foo::from(("Mary",16));
Butlet'ssaywewantanexplicit newconstructorfunctionontype Foo.Inthatcase,we
couldwriteitusingthe Intotrait:
implFoo{
pubfnnew<T>(v:T)->FoowhereT:Into<Foo>{
letresult=Foo::foo(v);
//wecouldcodeherethatwedohereaftermakingFooby
whatevermeans
result
}
}
Since Fromimplies Intowecanjustcalltheconstructorlikeso:
letf=Foo::new("Bob");
letf=Foo::new(("Mary",16));
Ifyoupreferyoucouldimplement Intobutit'smoretrickysinceitconsumestheinput,
whichmightnotbewhatyouwant.
Polymorphism
128

//ThisIntoworksonastringslice
implInto<Foo>for&'staticstr{
fninto(self)->Foo{
//...constructor
}
}
//ThisIntoworksonatupleconsistingofastringsliceanda
u16
implInto<Foo>for(&'staticstr,u16){
fninto(self)->Foo{
//...constructor
}
}
//...
letf:Foo="Bob".into();
letf:Foo=("Mary",16).into();
//OR
letf=Foo::new("Bob");
letf=Foo::new(("Mary",16));
Useenums
RememberthatanenumerationinRustcancontainactualdata,sowecouldalsoimplement
afunctionthattakesanenumerationasanargumentthathasvaluesforeachkindofvalueit
accepts:
Polymorphism
129

pubenumFooCtorArgs{
String(String),
StringU16(String,u16)
}
implFoo{
pubfnnew(v:FooCtorArgs){
matchv{
FooCtorArgs::String(s)=>{/*...*/}
FooCtorArgs::StringU16(s,i)=>{/*...*/}
}
}
}
//...
letf=Foo::new(FooCtorArgs::String("Bob".to_string()));
letf=Foo::new(FooCtorArgs::StringU16("Mary".to_string(),
16));
Polymorphism
130

ErrorHandling
C++allowscodetothrowandcatchexceptions.Asthenamesuggests,exceptionsindicate
anexceptionalerror.Anexceptionisthrowntointerruptthecurrentflowoflogicandallows
somethingfurtherupthestackwhichtocatchtheexceptionandrecoverthesituation.If
nothingcatchesthethrowthenthethreaditselfwillexit.
voiddo_something(){
if(!read_file()){
throwstd::runtime_error("read_filedidn'twork!");
}
}
...
try{
do_something();
}
catch(std::exceptione){
std::cout<<"Caughtexception--"<<e.what()<<std::endl;
}
Mostcodingguidelineswouldsaytouseexceptionssparinglyfortrulyexceptionalsituations,
andusereturncodesandotherformsoferrorpropagationforordinaryfailures.However
C++hasnosimplewaytoconfererrorinformationforordinaryfailuresandexceptionscan
becomplicatedtofollowandcancausetheirownissues.
Rustdoesnotsupportexceptions.Rustprogramsareexpectedtouseatypesuchas
Optionor Resulttopropagateerrorstotheircaller.Inotherwords,thecodeisexpected
toanticipateerrorsandhavecodetodealwiththem.
The Optionenumeitherreturns Noneor Somewherethe Someisapayloadofdata.It'sa
genericenumthatspecifiesthetypeofwhatitmaycontain:
enumOption<T>{
None
Some(T)
}
ErrorHandling
131

Forexample,wemighthaveafunctionthatsearchesadatabaseforaperson'sdetails,and
iteitherfindsthemoritdoesn't.
structPerson{/*...*/}
fnfind_person(name:&str){
letrecords=run_query(format!("select*frompersonswhere
name={}",sanitize_name(name)));
ifrecords.is_empty(){
None
}
else{
letperson=Person::new(records[0]);
Some(person)
}
}
The Resultenumeitherreturnsavalueofsometypeoranerrorofsometype.
enumResult<T,E>{
Ok(T),
Err(E)
}
Sowemighthaveafunction set_thermostatforsettingtheroomtemperature.
ErrorHandling
132

fnset_thermostat(temperature:u16)->Result<(),String>{
iftemperature<10{
err(format!("Temperature{}istoolow",temperature))
}
elseiftemperature>30{
err(format!("Temperature{}istoohigh",temperature))
}
else{
Ok(())
}
}
//...
letresult=set_thermostat();
ifresult.is_ok(){
//...
}
Thisfunctionwillreturnaunity ()valueforsuccess,ora Stringforfailure.
The?directive
Let'ssayyouhave2functions delete_userand find_user.Thefunction delete_userfirst
calls find_usertoseeiftheuserevenexistsandthenproceedstodeletetheuserorreturn
theerrorcodethatitgotfrom find_user.
ErrorHandling
133

fndelete_user(name:&str)->Result<(),ErrorCode>{
letresult=find_user(name);
ifletOk(user)=result{
//...deletetheuser
Ok(())
}
else{
Err(result.unwrap_err())
}
}
fnfind_user(name:&str)->Result<User,ErrorCode>{
//...findtheuserOR
Err(ErrorCode::UserDoesNotExist)
}
Wehavealotofcodein delete_usertohandlesuccessorfailurein find_userandthrow
itsfailurecodeupwards.SoRustprovidesaconvenience ?markontheendofthecallto
afunctionthatinstructsthecompilertogeneratetheif/elsebranchwehandwroteabove,
reducingthefunctiontothis:
fndelete_user(name:&str)->Result<(),ErrorCode>{
letuser=find_user(name)?;
//...deletetheuser
Ok(())
}
Providingyouwanttopropogateerrorsupthecallstack,thiscaneliminatealotofmessy
conditionaltestinginthecodeandmakeitmorerobust.
OlderversionsofRustusedaspecial try!()macroforthissamepurpose(nottobe
confusedwith try-catchinC++)whichdoesthesamething.Soifyouseecodelikethis,it
wouldbethesameasabove.
fndelete_user(name:&str)->Result<(),ErrorCode>{
letuser=try!(find_user(name));
//...deletetheuser
Ok(())
}
ErrorHandling
134

Nuclearoption-panic!()
Ifcodereallywantstodosomethingequivalenttoathrow/catchinC++itmaycallpanic!().
ThisisNOTrecommendedfordealingwithregularerrors,onlyirregularonesthatthecode
hasnowayofdealingwith.
Thismacrowillcausethethreadtoabortandifthethreadisthemainprogrammethread,
theentireprocesswillexit.
Apanic!()canbecaughtandshouldbeifRustisbeinginvokedfromanotherlanguage.The
waytocatchanunwindingpanicisaclosureatthetopmostpointinthecodewhereitcan
behandled.
usestd::panic;
letresult=panic::catch_unwind(||{
panic!("Badthings");
});
ErrorHandling
135

LambdaExpressions/Closures
LambdasinC++11
Alambdaexpression,orlambdaisananonymousfunctionthatcanbedeclaredandpassed
aroundfromwithinthescopeofthecallitself.
Thiscanbeparticularlyusefulwhenyouwanttosort,filter,searchorotherwisedosome
trivialsmallactionwithoutthebotherofdeclaringandmaintainingaseparatefunction.
InC++alambdalookslikethis:
floatvalues[10]={9,3,2.1,3,4,-10,2,4,6,7};
std::sort(values,values+10,[](floata,floatb){
returna<b;
});
Thislambdaispassedtoastd::sortfunctiontosortanarrayofvaluesbysomecriteria.
AC++lambdacan(butdoesn'thaveto)capturevariablesfromtheenclosingscopeifit
wishesanditcanspecifycaptureclausesinthe []sectionthatdefinehowcaptureis
made.Capturescanbebyvalueorreference,andcanexplicitlylistthevariablestocapture,
orspecifytocaptureeverythingbyreferenceorassignment.Alambdathatcaptures
variableseffectivelybecomesaclosure.
autov1=10.;
autov2=2.;
//Capturebyvalue
automultiply=[v1,v2](){returnv1*v2;};
//Capturebyreference
autosum=[&v1,&v2](){returnv1+v2;};
cout<<multiply()<<endl;
cout<<sum()<<endl;
v1=99;//Nowv1insum()references99
cout<<multiply()<<endl;
cout<<sum()<<endl;
LambdaExpressions/Closures
136

Wecanseefromtheoutputthat multiply()hascapturedcopiesofthevaluesin v1and
v2,whereas sum()capturesbyreferenceandsoitissensitivetochangesinthe
variables:
20
12
20
101
Acapturecanalsospecifyadefaultcapturemodebyspecifying =inthecaptureclauseor
byreference &andthenspecifycapturebehaviourforspecificvariables.
Soourcapturesabovecouldbesimplifiedto:
//Capturebyvalue
automultiply=[=](){returnv1*v2;};
//Capturebyreference
autosum=[&](){returnv1+v2;};
NotethatC++lambdascanexhibitdangerousbehaviour-ifalambdacapturesreferences
tovariablesthatgooutofscope,thelambda'sbehaviourisundefined.Inpracticethatcould
meantheapplicationcrashes.
ClosuresinRust
Rustimplementsclosures.Aclosureislikealambdaexceptitautomaticallycaptures
anythingitreferencesfromtheenclosingenvironment.i.e.bydefaultitcanaccessany
variablethatisintheenclosingscope.
HereisthesamesortsnippetwesawinC++expressedasRust.Thisclosuredoesn't
borrowanythingfromitsenclosingscopebutitdoestakeapairofargumentstocompare
twovaluesforsorting.The sort_by()functionrepeatedlyinvokestheclosuretosortthe
array.
usestd::cmp::Ord;
letmutvalues=[9.0,3.0,2.1,3.0,4.0,-10.0,2.0,4.0,
6.0,7.0];
values.sort_by(|a,b|a<b);
println!("values={:?}",values);
LambdaExpressions/Closures
137

Aclosurethatusesavariablefromtheenclosingscopeborrowsitbydefault.Thatmeans
theborrowedvariablecan'tchangewhiletheclosureisinscope.Tochangethevaluewe
mustensuretheclosuregoesoutofscopetofreetheborrow,e.g.withablock:
letmutx=100;
{
letsquare=||x*x;
println!("square={}",square());
}
x=200;
Alternativelyyoucan movevariablesusedbytheclosuresoitownsthemandtheybecome
inaccessiblefromtheouterscope.Sinceourclosurewasaccessinganinteger,themove
becomesanimplicitcopy.Soour squareclosurehasitsown xassignedthevalue 100.
Evenifwechange xintheouterscopeto 200,theclosurehasitsownindependentcopy.
letmutx=100;
letsquare=move||x*x;
println!("square={}",square());//10000
x=200;
println!("square={}",square());//10000
ThisistheequivalenttotheC++codeabovethatusedlambdaexpressionstobindtocopies
andreferences:
letmutv1=10.0;
letv2=2.0;
letmultiply=move||v1*v2;
letsum=|x:&f64,y:&f64|x+y;
println!("multiply{}",multiply());
println!("sum{}",sum(&v1,&v2));
v1=99.0;
println!("multiply{}",multiply());
println!("sum{}",sum(&v1,&v2));
ThiswillyieldthesameresultsastheC++code.Themaindifferencehereisthatratherthan
bindingourclosuretoareference,wepassedthereferencevaluesinasparameterstothe
closure.
LambdaExpressions/Closures
138

LambdaExpressions/Closures
139

Templates/Generics
C++offerstemplatesasawaytowritegenericcodeusinganabstracttypeandthen
specializeitbysubstitutingoneormoretypesintoaconcreteclass.
template<typenameT>
inlinevoiddebug(constT&v){
cout<<"Thevalueofobjectis"<<v<<endl;
}
//...
debug(10);
Thistemplateusesthetypeoftheparameter(intthiscase10)tocreateaninlinefunction
thatprintsoutthevalueofthattype:
Thevalueofobjectis10
Classescanalsobemadefromtemplates:
template<classT>
classStack{
private:
vector<T>elements;
public:
voidpush(constT&v){
//...
}
Tpop(){
//...
}
}
//...
Stack<double>doubleStack;
Thisclassimplementsasimplestackusingatemplatetoindicatethetypeofobjectit
contains.
Templates/Generics
140

ThisisaverypowerfulmechanismandtheC++librarymakesextensiveuseofit.
Wheretemplatescanbecomeabitofamessisthatthetemplatesareinlineandthe
compilerwillexpandoutanythingyoucallbeforeattemptingtocompileit.
Aninnocuouserrorsuchasusingatypethathasnodefaultcopyconstructorinacollection
cancausethecompilertogonutsandoutputawallofindecipherableerrors.
GenericFunctions
Rust'sequivalenttoatemplateiscalledageneric.Agenericgeneralizesafunctionoratrait
soitworkswithdifferenttypesthatmatchthecriteria.
SotheRustequivalentofthe debug()functioninC++wouldbethis.
usestd::fmt;
fndebug<T>(data:T)whereT:fmt::Display{
println!("Thevalueofobjectis{}",data);
}
//...
debug(10);
Herewedescribeafunctionthattakesagenerictype Twheretheconstraintisthat T
mustimplementthetrait std::fmt::Display.Anystructthatimplementsthistraitcan
passedintothecall.Sinceintegertypesimplementthetrait,wecanjustcallitdirectlyas
debug(10)andthecompilerishappy.
Genericstructs
Similarlywecanusegenericsonastruct.SotheequivalentinRustoftheC++template
class Stackisthis:
Templates/Generics
141

structStack<T>{
elements:Vec<T>
}
impl<T>Stack<T>{
fnnew()->Stack<T>{Stack{elements:Vec::new()}}
fnpush(v:T){
//...
}
fnpop()->Option<T>{
//...
None
}
}
//...
letdouble_stack:Stack<f64>=Stack::new();
Whereclause
The whereclausecanbeaddedtoimposeconstraintsonwhatgenerictypemustdotobe
allowedtobesuppliedtothegenericfunctionorstruct.
Forexamplewemighthaveafunctionthattakesaclosureasanargument.Aclosureisa
functionandsowewanttodefinetheshapethattheclosurewilltake.
So:
fncompare<T,F>(a:T,b:T,f:F)->bool
whereF:FnOnce(T,T)->bool
{
f(a,b)
}
letcomparer=|a,b|a<b;
letresult=compare(10,20,comparer);
Templates/Generics
142

Herewehavedefineda compare()functionthattakesacoupleofvaluesofthesametype.
The whereclausestatesthatthefunctionmusttaketwovaluesofthesametypeandreturn
aboolean.Thecompilerwillensureanyclosurewepassinmatchesthatcriteria,asindeed
our comparerclosuredoes.
Templates/Generics
143

Attributes
C++hasvariouswaystogivecompilerdirectivesduringcompilation:
Compileflagsthatcontrolnumerousbehaviours
#pragmastatements- once, optimize, comment, packetc.Somepragmassuchas
commenthavebeenwildlyabusedinsomecompilerstoinsert"comments"intoobject
filesthatcontroltheimport/exportofsymbols,staticlinkingandotherfunctionality.
#definewithubquitous #ifdef/ #else/ #endifblocks
Keywords inline, const, volatileetc..Thesehintthecodeandallowthecompiler
tomakedecisionsthatmightchangeitsoutputoroptimization.Compilersoftenhave
theirownproprietaryextensions.
Rustusesanotationcalledattributesthatservesasimilarroletoallofthesethingsbutina
moreconsistentform.
Anattribute #[foo]appliestothenextitemitisdeclaredbefore.Acommonattributeis
usedtodenoteaunittestcasewith #[test]:
#[test]
fnthis_is_a_test(){
//...
}
Attributescanalsobeexpressedas #![foo]whichaffectsthethingthey'recontainedby
ratherthethingthatfollowsthem.
fnthis_is_a_test(){
#![test]
//...
}
Attributesareenclosedina #[]blockandprovidecompilerdirectivesthatallow:
Functionstobemarkedasunitorbenchmarktests
FunctionstobemarkedforconditionalcompilationforatargetOS.Afunctioncanbe
definedthatonlycompilesforonetarget.e.g.perhapsthecodethatcommunicateswith
anotherprocessonWindowsandLinuxisencapsulatedinthesamefunctionbut
implementeddifferently.
Attributes
144

Enable/disablelintrules
Enable/disablecompilerfeatures.Certainfeaturesofrustmaybeexperimentalor
deprecatedandmayhavetobeenabledtobeaccessed.
Changetheentrypointfunctionfrom maintosomethingelse
Conditionalcompilationaccordingtotargetarchitecture,OS,family,endianess,pointer
width
Inlinehinting
Derivingcertaintraits
Enablingcompilerfeaturessuchaspluginsthatimplementproceduralmacros.
Importingmacrosfromothercrates
Usedbycertaincrateslikeserdeandrockettoinstrumentcode-NBRocketuses
unstablecompilerhooksforthisandinsodoinglimitsitselftoworkinginnightlybuilds
only.
Conditionalcompilation
Conditionalcompilationallowsyoutotestthetargetconfigurationsandoptionallycompile
functionsormodulesinornot.
Themainconfigurationsyouwilltestinclude:
Targetarchitecture-"x86","x86_64",mips","arm"etc.
TargetOS-"windows","macos","ios","linux","android","freebsd"etc.
Targetfamily-"unix"or"windows"
Targetenvironment-"gnu","msvc"etc
Targetendianess
Targetpointerwidth
SoifyouhaveafunctionwhichisimplementedonewayforWindowsandanotherforLinux
youmightcodeitlikeso:
#[cfg(windows)]
fnget_app_data_dir()->String{/*...*/}
#[cfg(not(windows))]
fnget_app_data_dir()->String{/*...*/}
Manymorepossibilitiesarelistedinthedocumentation.
Linkingtonativelibraries
Attributes
145

InC/C++codeisfirstcompiledandthenitislinked,eitherbyadditionalargumentstothe
compiler,orbyinvokingalinker.
InRustmostofyourlinkingistakencareforyouprovidingyouuse cargo.
1. Allyoursourcesarecompiledandlinkedtogether.
2. Externalcratesareautomaticallybuiltasstaticlibsandlinkedin.
3. ButifyouhavetolinkagainstsomethingexternalthroughFFIyouhavetowritea
#linkdirectiveinyour lib.rsor main.rs.Thisissomewhatanalogoustothe
#pragma(comment,"somelib")inC++.
C++ Rust
#pragma(comment,"somelib") #[link(name="somelib")]
-#[link(name="somelib",kind="static")]
Thedefaultkindfor #linkis dynamiclibrarybut staticcanbeexplicitlystatedspecified.
Inliningcode
Inlininghappenswhereyourfunctionlogicisinsertedin-placetothecodethatinvokesit.It
tendstohappenwhenthefunctiondoessomethingtrivialsuchasreturnavalueorexecute
asimpleconditional.Theoverheadofduplicatingthecodeisoutweighedbythe
performancebenefit.
InliningisachievedinC++bydeclaringandimplementingafunction,classmethodor
templatemethodinaheaderormarkingitwiththeinlinekeyword.
InRust,inliningisonlyahint.Rustrecommendsnotforcinginlning,ratherleavingitasahint
fortheLLVMcompilertodowithasitseesfit.
C++ Rust
Explicitlywith inlineorimplicitlythroughmethods
implementedinclass/struct
#[inline], #[inline(always)],
#[inline(never)]
Anotheralternativetoexplicitlyinliningcodeistousethelink-timeoptimisationinLLVM.
rustc-Clto
Attributes
146

Multithreading
Multithreadingallowsyoutorunpartsofyourprogrammingconcurrently,performingtasksin
parallel.Everyprogramhasamainthread-i.e.theoneyour main()startedfrom,in
additiontowhichareanythatyoucreate.
Examplesofreasonstousethreads:
Longrunningoperations,e.g.zippingupalargefile.
Activitythatisblockinginnature,e.g.listeningforconnectionsonasocket
Processingdatainparallel,e.g.physics,collisiondetectionetc.
Asynchronousactivities,e.g.timers,pollingoperations.
Inaddition,ifyouuseagraphicaltoolkit,or3rdpartylibrariestheymayspawntheirown
threadsthatyoudonotknowabout.
Threadsafety
Onewordyouwillhearalotinmultithreadingisthreadsafety.
Bythatwemean:
Threadsshouldnotbeabletomodifythedataatthesametime.Whenthishappensitis
calledadataraceandcancorruptthedata,causingacrash.e.g.twothreadstryingto
appendtoastringatthesametime.
Threadsmustnotlockresourcesinawaythatcouldcausedeadlocki.e.thread1
obtainsalockonresourceBandblocksonresourceA,whilethread2obtainsalockon
resourceAandblocksonresourceB.Boththreadsarelockedforeverwaitingfora
resourcetoreleasethatneverwillbe.
Raceconditionsarebad,i.e.theorderofthreadexecutionproducesunpredictable
resultsontheoutputfromthesameinput.
APIsthatcanbecalledbymultiplethreadsmusteitherprotecttheirdatastructuresor
makeitanexplicitproblemoftheclienttosortout.
Openfilesandotherresourcesthatareaccessedbymultiplethreadsmustbemanaged
safely.
Protectingshareddata
Datashouldneverbereadatthesametimeitiswrittentoinanotherthread.Norshould
databewrittentoatthesametimebytwothreads.
Multi-threading
147

Thecommonwaytopreventthisiseither:
Useamutextoguardaccesstothedata.Amutexisaspecialclassthatonlyone
threadcanlockatatime.Otherthreadsthattrytolockthemutexwillwaituntilthelock
heldbyanotherthreadisrelinquished
Usearead-writelock.Similartoamutex,itallowsonethreadtolockthethreadfor
writingdata,howeveritpermitsmultiplethreadstohavereadaccess,providingnothing
isalreadywritingtoit.Fordatathatisreadmorefrequentlythanitismodified,thisisa
lotmoreefficientthanjustamutex.
Avoidingdeadlock
Thebestwaytoavoiddeadlockisonlyeverobtainalocktoonethingeverandreleaseitas
soonasyouaredone.Butifyouhavetolockmorethanonething,ensurethelockingorder
isconsistentbetweenallyourthreads.Soifthread1locksAandB,thenensurethatthread
2alsolocksAandBinthatorderandnotBthenA.Thelatterissurelygoingtocausea
deadlock.
C/C++
CandC++predatethreadingtosomeextentsountilC++11thelanguageshavehadlittle
built-insupportformulti-threadingandwhattherewastendedtobecompilerspecific
extensions.
AconsequenceofthisisthatCandC++haveZEROENFORCEMENTofthreadsafety.If
youdatarace-toobad.Ifyouforgettowritealockinonefunctionevenifyouremembered
alltheothers-toobad.Youhavetodisciplineyourselftothinkconcurrentlyandapplythe
properprotectionswhereitisrequired.
Theconsequenceofnotdoingsomaynotevenbefeltuntilyoursoftwareisinproduction
andthatonecustomerstartscomplainingthattheirserverfreezesaboutonceaweek.Good
luckfindingthatbug!
MultithreadingAPIs
ThemostcommonAPIswouldbe:
<thread>, <mutex>-fromC++11onwards
POSIXthreads,orpthreads.ExposedbyPOSIXsystemssuchasLinuxandmostother
Unixderivatives,e.g.OSX.Thereisalsopthread-win32supportbuiltoverthetopof
Win32threads.
Win32threads.ExposedbytheWindowsoperatingsystem.
Multi-threading
148

OpenMP.SupportedbymanyC++compilers.
3rdpartylibrarieslikeBoostandQtprovidewrappersthatabstractthedifferences
betweenthreadAPIs.
AllAPIswillhaveincommon:
Threadcreation,destruction,joins(waitingonthreads)anddetaches(freeingthethread
todowhatitlikes).
Synchronizationbetweenthreadsusinglocksandbarriers.
Mutexes-mutualexclusionlocksthatprotectshareddata.
Conditionalvariables-ameanstosignalandnotifyofconditionsbecomingtrue.
std::thread
The std::threadrepresentsasinglethreadofexecutionandprovidesanabstractionover
platformdependentwaysofthreading.
#include<iostream>
#include<thread>
usingnamespacestd;
voidDoWork(intloop_count){
for(inti=0;i<loop_count;++i){
cout<<"Helloworld"<<i<<endl;
}
}
intmain(){
threadworker(DoWork,100);
worker.join();
}
Theexamplespawnsathreadwhichinvokesthefunctionandpassestheparameterintoit,
printingamessage100times.
std::mutex
C++providesafamilyofvarious mutextypestoprotectaccesstoshareddata.
Multi-threading
149

Themutexisobtainedbya lock_guardandotherattemptstoobtainthemutexareblocked
untilthelockisrelinquished.
#include<iostream>
#include<thread>
#include<mutex>
usingnamespacestd;
mutexdata_guard;
intresult=0;
voidDoWork(intloop_count){
for(autoi=0;i<loop_count;++i){
lock_guard<mutex>guard(data_guard);
result+=1;
}
}
intmain(){
threadworker1(DoWork,100);
threadworker2(DoWork,150);
worker1.join();
worker2.join();
cout<<"result="<<result<<endl;
}
POSIXthreads
ThepthreadsAPIisprefixed pthread_andworkslikeso:
Multi-threading
150

#include<iostream>
#include<pthread.h>
usingnamespacestd;
void*DoWork(void*data){
constintloop_count=(int)data;
for(inti=0;i<loop_count;++i){
cout<<"Helloworld"<<i<<endl;
}
pthread_exit(NULL);
}
intmain(){
pthread_tworker_thread;
intresult=pthread_create(&worker_thread,NULL,DoWork,
(void*)100);
//Waitforthethreadtoend
result=pthread_join(worker_thread,NULL);
}
ThisexamplespawnsathreadwhichinvokesDoWorkwiththepayloadof100whichcauses
thefunctiontoprintamessage100times.
Win32Threads
Win32threadinghasfunctionsanalogoustothoseinPOSIX.Theyhavenamessuchas
CreateThread, ExitThread, SetThreadPriorityetc.
OpenMPAPI
OpenMulti-Processing(OpenMP)isanAPIformulti-threadedparallelprocessing.OpenMP
reliesoncompilersupportbecauseyouusespecial #pragmadirectivesinyoursourceto
controlthreadcreationandaccesstodata.
GCC,ClangandVisualC++havesupportforOpenMPsoitisanoption.
OpenMPisacomplexstandardbuttheuseofdirectivescanmakeforcleanercodethan
invokingthreadingAPIsdirectly.Thedownsideisitisalsomoreopaquehidingwhatthe
softwareisdoing,makingitconsiderablymoredifficulttodebug.
Multi-threading
151

OpenMPisdescribedindetailattheOpenMPwebsite.
Threadlocalstorage
Threadlocalstorage,orTLSisstaticorglobaldatawhichisprivatetoeverythread.Each
threadholdsitsowncopyofthisdatasoitcanmodifyitwithoutfearofcausingadatarace.
Compilersalsohaveproprietarywaystodecoratetypesasthreadlocal:
__threadintprivate;//gcc/clang
__declspec(thread)intprivate;//MSVC
C++11hasgaineda thread_localdirectivetodecoratevariableswhichshoulduseTLS.
thread_localintprivate
Rust
WesawwithC++thatyouhadtobedisciplinedtoremembertoprotectdatafromrace
conditions.
Rustdoesn'tgiveyouthatluxury-
1. Anydatathatyousharemustbeprotectedinathreadsafefashion
2. Anydatathatyoupassbetweenthreadsmustbemarkedthreadsafe
Spawningathread
Spawningathreadiseasyenoughbycalling spawn,supplyingtheclosureyouwanttorun
inthecontextofyournewthread.
usestd::thread;
thread::spawn(move||{
println!("Hello");
});
Alternativelyyoucansupplyafunctionto spawnwhichiscalledinthesamemanner.
Multi-threading
152

fnmy_thread(){
println!("Hello");
}
//...
thread::spawn(my_thread);
Ifyousupplyaclosurethenitmusthavealifetimeof 'staticbecausethreadscanoutlive
thethingthatcreatedthem.i.e.theyaredetachedbydefault.
Aclosurecanmakeuseofmovevaluesthataremarked Sendsothecompilerallows
ownershiptotransferbetweenthreads.
Likewisefunction/closuremayalsoreturnavaluewhichismarked Sendsothecompiler
cantransferownershipbetweentheterminatingthreadandthethreadwhichcalls jointo
obtainthevalue.
Sothethreadaboveisdetached.Ifwewantedtowaitforthethreadtocomplete,the spawn
returnsa JoinHandlethatwecancall jointowaitfortermination.
leth=thread::spawn(move||{
println!("Hello");
});
h.join();
Iftheclosureorfunctionreturnsavalue,wecanuse jointoobtainit.
leth=thread::spawn(move||100*100);
letresult=h.join().unwrap();
println!("Result={}",result);
Dataraceprotectioninthecompiler
Dataracesarebadnews,butfortunatelyinRustthecompilerhasyourback.YouMUST
protectyourshareddataoritwon'tcompile.
Thesimplestwaytoprotectyourdataistowrapthedatainamutexandprovideeach
threadinstancewithareferencecountedcopyofthemutex.
Multi-threading
153

letshared_data=Arc::new(Mutex::new(MySharedData::new()));
//EachthreadwespawnshouldhaveacloneofthisArc
letshared_data=shared_data.clone();
thread::spawn(move||{
letmutshared_data=shared_data.lock().unwrap();
shared_data.counter+=1;
});
Hereisafullexamplethatspawns10threadsthateachincrementthecounter.
Multi-threading
154

structMySharedData{
pubcounter:u32,
}
implMySharedData{
pubfnnew()->MySharedData{
MySharedData{
counter:0
}
}
}
fnmain(){
spawn_threads();
}
fnspawn_threads(){
letshared_data=Arc::new(Mutex::new(MySharedData::new()));
//Spawnanumberofthreadsandcollecttheirjoinhandles
lethandles:Vec<JoinHandle<_>>=(0..10).map(|_|{
letshared_data=shared_data.clone();
thread::spawn(move||{
letmutshared_data=shared_data.lock().unwrap();
shared_data.counter+=1;
})
}).collect();
//Waitforeachthreadtocomplete
forhinhandles{
h.join();
}
//Printthedata
letshared_data=shared_data.lock().unwrap();
println!("Total={}",shared_data.counter);
}
Sothebasicstrategywillbethis:
Multi-threading
155

1. Everythreadwillgetit'sownatomicreferencetothemutex.
2. Eachthreadthatwishestoaccessthesharedmustobtainalockonthemutex.
3. Oncethelockisreleased,thenextwaitingthreadcanobtainaccess.
4. ThecompilerwillenforcethisandgenerateerrorsifANYTHINGiswrong.
ReadWriteLock
Areadwritelockworksmuchlikeamutex-wewraptheshareddataina RwLock,andthen
inan Arc.
letshared_data=Arc::new(RwLock::new(MySharedData::new()));
Eachthreadwilltheneitherneedtoobtainareadlockorawritelockontheshareddata.
letshared_data=shared_data.read().unwrap();
//OR
letmutshared_data=shared_data.write().unwrap();
Theadvantageofa RwLockisthatmanythreadscanconcurrentlyreadthedata,providing
nothingiswritingtoit.Thismaybemoreefficientinmanycases.
Sendingdatabetweenthreadsusingchannels
TODOmpscchannel
Threadlocalstorage
AswithC++youmayhavereasontousethreadlocalstorage
thread_local!{
//TODO
}
Usefulcrates
Rayon
Multi-threading
156

Therayoncrateimplementsparalleliteratorsthatallowyourcollectionstobeiteratedin
parallel.Thecrateutilisesworkstealinganddivideandconqueralgorithmscoupletoa
threadpooltoprocesscollectionsmorequicklythantheycouldbeinasequentialfashion.
Generallyspeakingthisisadrop-inreplacementwiththeexceptionthatyoucall par_iter
insteadof iter.Thecrateimplementsa ParallelIteratortraitoncollectionclasses.
userayon::prelude::*;
fnsum_of_squares(input:&[i32])->i32{
input.par_iter()
.map(|&i|i*i)
.sum()
}
Seethecratesiteformoreinformation.
Multi-threading
157

Lint
C/C++compilerscanissuemanyusefulwarningsbuttheamountofstaticanalysistheycan
doisusuallyquitelimited.
TheRustcompilerperformsafarmorerigorouslifecyclecheckondataandthenfollowsup
withalintcheckthatinspectsyourcodeforpotentiallybadorerroneous
Inparticularitlooksfor:
Dead/unusedcode
Unreachablecode
Deprecatedmethods
Undocumentedfunctions
Camelcase/snakecaseviolations
Unboundedrecursioncode(i.e.noconditionalstostoprecursion)
Useofheapmemorywhenstackcouldbeused
Unusedexterncrates,imports,variables,attributes,mut,parentheses
Using"whiletrue{}"insteadof"loop{}"
Lintrulescanbeenforcedmorestrictlyorignoredbyusingattributes:
#[allow(rule)]
#[warn(rule)]
#[deny(rule)]
#[forbid(rule)]
Afulllistoflintrulescanbefoundbytyping"rustc-Whelp":
Lint
158

namedefaultmeaning
------------------
box-pointersallowuseofowned(Boxtype)
heapmemory
fat-ptr-transmutesallowdetectstransmutesof
fatpointers
missing-copy-implementationsallowdetectspotentially-
forgottenimplementationsof`Copy`
missing-debug-implementationsallowdetectsmissing
implementationsoffmt::Debug
missing-docsallowdetectsmissing
documentationforpublicmembers
trivial-castsallowdetectstrivialcasts
whichcouldberemoved
trivial-numeric-castsallowdetectstrivialcastsof
numerictypeswhichcouldberemoved
unsafe-codeallowusageof`unsafe`code
...
Therearealotchecksthanarelistedhere.
Lint
159

Macros
C/C++Preprocessor
Clanguagesarelittleunusualinthattheyarecompiledintwophases.Thefirstphaseis
calledthepreprocess.Inthisphase,thepreprocessorlooksfordirectivesstartingwitha#
symbolandrunsstringsubstitutionandconditionalinclusion/exclusionbasedonthose
directives.Onlyafterthefilehasbeenpreprocesseddoesthecompilerattempttocompileit.
Preprocessordirectivesstartwitha #symbol.Forexamplethe #definedirectivecreates
amacrowithanoptionalvalue:
#defineIS_WINDOWS
#defineSHAREWARE_VERSION1
We'llexploremacrosmoreinamoment.Anotherdirectiveisthe #if\#else\#endifor
#ifdef\#else\#endifwhichcanbeusedtoincludecodefromonebranchortheotherofa
testaccordingtowhatmatches.
#ifSHAREWARE_VERSION==1
showNagwarePopup();
#endif
//...
#ifdefIS_WINDOWS
writePrefsToRegistry();
#else
writePrefsToCfg();
#endif
Anotherdirectiveis #include.InCandC++,publicfunctionsandstructuresaretypically
definedandimplementedinseparatefiles.The #includedirectiveallowsaheadertobe
pulledintothefrontofanyfilethatmakesuseofthosedefinitions.
Macros
160

//System/externalheaderstendtouseanglestyle
#include<string>
#include<stdio.h>
//Localheaderstendtousedoublequotes
#include"MyClass.h"
TheimportantthingtorememberinallofthisisALLofthesethingshappenbeforethe
compilerevenstarts!Your main.cmightonlybe10linesofcodebutifyou #includesome
headersthepreprocessormaybefeedingmanythousandsoflinesoftypes,functionsinto
thecompiler,allofwhichareevaluatedbeforetheygettoyourcode.
C/C++Macros
Macrosarestringsubstitutionpatternsperformedbythepreprocessorbeforethesourceis
compiled.Assuchtheycanbeverypronetoerrorandsohavebeendeprecatedinfavourof
constantsandinlinefunctions.
Hereisasimplemacrothatwouldbehaveinanunexpectedmanner:
#defineMULTIPLY(x,y)x*y
//
intx=10,y=20;
intresult=MULTIPLY(x+1,x+y);
//ValueisNOT330(11*30),it's41becausemacrobecomesx+
1*x+y
Themacroisverysimple-multiplyxbyy.Butitfailsifeitherargumentisanexpression.
Judicioususeofparenthesesmightavoidtheerrorinthiscase,butwecouldbreakitagain
usingsomepreorpostincrements.
MacrosinC++arealsounhygenic,i.e.themacrocaninadvertentlyconflictwithorcapture
valuesfromoutsideofitselfcausingerrorsinthecode.
#defineSWAP(x,y)inttmp=y;y=x;x=y;
//
inttmp=10;
inta=20,b=30;
SWAP(a,b);//ERROR
Macros
161

HereourSWAPmacrousesatemporaryvaluecalled tmpthatalreadyexistedinthescope
andsothecompilercomplains.Amacromightavoidthisbyusingshadowvariables
enclosedwithina do/while(0)blocktoavoidconflictsbutitislessthanideal.
#defineSWAP(x,y)do{inttmp=y;y=x;x=y}while(0);
Consequentlyinlinefunctionsareusedwhereverpossible.Evensomacrosarestill
frequentlyusedintheseroles:
Toconditionallyincludeforacommand-lineflagordirective,e.g.thecompilermight
#defineWIN32socodecanconditionallycompileonewayoranotheraccordingtoits
presence.
Foraddingguardblocksaroundheaderstopreventthembeing#include'dmorethan
once.Mostcompilersimplementa"#pragmaoncedirective"whichisanincreasingly
commonalternative
Forgeneratingsnippetsofboilerplatecode(e.g.namespacewrappers),orthingsthat
mightbecompiledawaydependingon#defineslikeDEBUGbeingsetornot.
Formakingstringsofvaluesandotheresotericedgecases
Writingamacroiseasy,perhapstooeasy:
#definePRINT(x)\
printf("Youprinted%d",x);
Thismacrowouldexpandtoprintfbeforecompilationbutitwouldfailtocompileorprintthe
wrongthingifxwerenotaninteger.
Rustmacros
MacrosinRustarequiteacomplextopicbuttheyaremorepowerfulandsaferthanthe
onesinC++.
Rustmacrosarehygenic.Thatistosayifamacrocontainsvariables,theirnamesdo
notconflictwith,hide,orotherwiseinterferewithnamedvariablesfromthescope
they'reusedfrom.
Thepatternsuppliedinbetweenthebracketsofthemacroaretokenizedand
designatedaspartsoftheRustlanguage.identifiers,expressionsetc.InC/C++you
can#defineamacrotobeanythingyoulikewhetheritisgarbageorsyntactically
correct.Furthermoreyoucancallitfromanywhereyoulikebecauseitispreprocessed
evenbeforethecompilerseesthecode.
Macros
162

Rustmacrosareeitherdeclarativeandrulebasedwitheachrulehavingalefthandside
pattern"matcher"andarighthandside"substitution".Orthey'reproceduralandactualy
rustcodeturnsaninputintoanoutput(seesectionbelow).
Macrosmustproducesyntacticallycorrectcode.
Declarativemacroscanbeexportedbycratesandusedinothercodeprovidingthe
othercodeelectstoenablemacrosupportfromthecrate.Thisisalittlemessysinceit
mustbesignalledwitha#[macro_export]directive.
Withallthatsaid,macrosinRustarecomplex-perhapstoocomplex-andgenerally
speakingshouldbeusedassparinglyaspossible.
Hereisasimpledeclarativemacrodemonstratingrepetitioncalledhello_x!().Itwilltakea
commaseparatedlistofexpressionsandsayhellotoeachoneofthem.
macro_rules!hello_x{
($($name:expr),*)=>(
$(println!("Hello{}",$name);)*
)
}
//Thecodecansupplyasmanyargumentsitlikestothismacro
hello_x!("Bob","Sue","John","Ellen");
Essentiallythematchermatchesagainstourcommaseparatelistandthesubstitution
generatesoneprintln!()withthemessageforeachexpression.
HelloBob
HelloSue
HelloJohn
HelloEllen
Whatifwethrewsomeotherexpressionsintothatarray?
hello_x!("Bob",true,1234.333,-1);
Wellthatworkstoo:
Macros
163

HelloBob
Hellotrue
Hello1234.333
Hello-1
Whataboutsomeillegalcode:
hello_x!(Aardvark{});
Wegetameaningfulerrororiginatingfromthemacro.
error[E0422]:`Aardvark`doesnotnameastructure
|
8|hello_x!(Aardvark{});
|^^^^^^^^
<stdmacros>:2:27:2:58note:inthisexpansionofformat_args!
<stdmacros>:3:1:3:54note:inthisexpansionofprint!
(definedin<stdmacros>)
<anon>:5:7:5:35note:inthisexpansionofprintln!(definedin
<stdmacros>)
<anon>:8:1:8:23note:inthisexpansionofhello_x!(definedin
<anon>)
Realworldexample-vec!()
Rustcomeswithalotofmacrosforreducingsomeofthelegworkoftediousboilerplate.
Forexamplethevec!()macroisawaytodeclareastd::Vecandprepopulateitwithsome
values.
Hereistheactualvec!macrosourcecodetakenfromtheRustsource:
Macros
164

macro_rules!vec{
($elem:expr;$n:expr)=>(
$crate::vec::from_elem($elem,$n)
);
($($x:expr),*)=>(
<[_]>::into_vec(box[$($x),*])
);
($($x:expr,)*)=>(vec![$($x),*])
}
Itlookscomplexbutwewillbreakitdowntoseewhatitdoes.Firstlyithasamatch-like
syntaxwiththreebranchesthatexpandtoanythingthatmatchesthelefthandside:
Firstbranch
Thefirstmatchermatchesapatternsuchas 1;100.Thevalue 1goesinto $elem,the
value 100goesinto $n:
($elem:expr;$n:expr)=>(
$crate::vec::from_elem($elem,$n)
);
The $crateisaspecialvaluethatresolvestothemodulecratewhichhappenstobestd.
Sothisexpandstothis:
letv=vec!(1;100);
//1stbranchmatchesanditbecomesthis
letv=std::vec::from_elem(1,100);
Secondbranch
Thesecondmatchercontainsaglobexpression-zeroormoreexpressionsseparatedby
comma(thelastcommaisoptional).Eachmatchingexpressionendsupin $x:
($($x:expr),*)=>(
<[_]>::into_vec(box[$($x),*])
);
Macros
165

Sowecanwrite:
letv=vec!(1,2,3,4,5);
//3ndbranchmatchesanditbecomesthis
letv=<[_]>::into_vec(box[1,2,3,4,5]);
TheboxkeywordtellsRusttoallocatethesuppliedarrayontheheapandmovesthe
ownershipbycallingahelperfunctioncalledintovec()thatwrapsthememoryarraywitha
Vecinstance.The<[\]>::atthefrontisaturbo-fishnotationtomaketheinto_vec()generic
functionhappy.
Thirdbranch
Thethirdbranchisalittleoddandalmostlooksthesameasthesecondbranch.Buttakeat
lookthecomma.Inthelastbranchitwasnexttotheasterisk,thistimeitisinsidetheinner
$().
($($x:expr,)*)=>(vec![$($x),*])
Thematchermatcheswhenthethecommaisthereandifsorecursivelycallsvec!()againto
resolvetothesecondbranchmatcher:
Basicallyitistheresothattherecanbeatrailingcommainourdeclarationanditwillstill
generatethesamecode.
//3rdbranchmatchesthis
letv=vec!(1,2,3,4,5,);
//anditbecomesthis
letv=vec!(1,2,3,4,5);
//whichmatches2ndbranchtobecome
letv=<[_]>::into_vec(box[1,2,3,4,5]);
ProceduralMacros
Sofarwe'vetalkedaboutdeclarativemacrosthatexpandouttobeRustcodebasedupon
howtheypatternmatchtherulesdefinedbythemacro.
Macros
166

Asecondkindofmacroistheproceduralmacro.Aproceduralmacroisapluginwrittenin
RustthatiscompiledandloadedbythecompilertoproducearbitraryRustcodeasits
output.
Aproceduralmacrocanthereforebethoughtofasacodegeneratorbutonethatformspart
oftheactualcompiler.Proceduralmacroscanbeparticularlyusefulfor:
Serialization/deserialization(e.g.theserdemodulegeneratescodeforreadingand
writingstructstoavarietyofformats-JSON,YAML,TOML,XMLetc.)
DomainSpecificLanguages(e.g.embeddedSQL,regularexpressionsetc).
Aspectorientedprogramming(e.g.extradebugging,performancemetricsetc)
Newlintandderiverules
FormoreinformationlookatthissectiononcompilerpluginsintheRustbook.
Otherformsofconditionalcompilation
WesawthattheC/C++preprocessorcanbeusedforconditionalcompilation.The
equivalentinRustisattributes.Seetheattributessectiontoseehowtheymaybeused.
Macros
167

Memoryallocation
Thissectionisconcernedwithmemoryallocation,i.e.creatingobjectsthatresideonthe
heapandnotonthestack,andthemannerinwhichtheyarecreatedandaredestroyed.
C++
CandC++havevariousstandardwaystoallocatememory:
1. malloc/calloc/realloc()and free()functions
2. newand delete(C++only)
3. new[]and delete[]forarrays(C++only)
Invoking malloc()/free()onaC++classorstructisneveragoodideasinceitwillnot
callthecorrespondingclassconstructorordestructor.The realloc()functionallocatesa
newpieceofmemory,copyingthecontentsofanexistingpieceofmemorybeforefreeing
theoriginal.
//malloc/free
char*buffer=(char*)malloc(1024);
...
free(buffer);
//new/delete
Stack*stack=newStack();
...
deletestack;
//new[]/delete[]
Node*nodes=newNode[100];
...
delete[]nodes;
Ineachcasetheallocationmustbematchedbythecorrespondingfreeactionso
immediatelywecanseescopeforerrorhere:
1. Ownershiprulescangetmessy,especiallywhenaclassispassedaroundalot-who
deletestheobjectandwhen?
2. Notusingthecorrect new& deletepair,causingamemoryleak.e.g.calling delete
insteadof delete[]
MemoryAllocation
168

3. Forgettingtofreememoryatallcausingamemoryleak.
4. Freeingmemorymorethanonce.
5. Callingadanglingpointer,i.e.apointerwhichreferstofreedmemory.
6. Allocating/freeinginawaythatcausesheapfragmentation.Reallocationcancause
fragmentationtohappenalotfaster.
C++hassmartpointerswhichmanagethelifetimeonobjectsandareagoodwayto
programmererror:
{
std::auto_ptr<Database>db(newDatabase());
//...objectisdeletedwhendbgoesoutofscope
}
//C++11
{
std::unique_ptr<Database>db(newDatabase());
//...objectisdeletedwhendbgoesoutofscope
std::unique_ptr<Node[]>nodes<newNode[100]);
//...arraysofobjectsaresupportedtoo
}
//C++11
{
std::shared_ptr<Database>db(newDatabase());
//Referencecountdb
setDatabase(db);
//...objectisdeletedwhenlastshared_ptrreferencetoit
goesoutofscope
std::shared_ptr<Node[]>nodes<newNode[100]);
//...arraysofobjectsaresupportedtoo
}
Unfortunatelyitisnotalwayspossibletousesmartpointersbutwhereverpossiblethey
shouldbeused.
Otherwaysofallocatingmemory
MemoryAllocation
169

VirtuallyeveryCandC++libraryhassolutionsformanagingmemory.Theyalltheirown
indivualconceptofownershipwhichisusuallydifferentfromonetothenext.BoostandQt
havetheirownmemorymanagement"smart"pointers.Qtevenrequirescertainobjectsto
bedeleted"later"byamessageprocessinglooponthethreadthatcreatedtheobject.Some
librariesevenadoptaCOM-likemodelofreferencecountingobjectswithsmartpointers.
MostClibrarieswillexposeanallocandfreefunctionforcreatinganddestroyingcontext
objectsthatcallerspasstotheAPI.
Memoryallocationcanevenbeoverwrittenandreplacedinsomecircumstances.InC,the
standardmalloc/freecanbesubstitutedforanothermemoryallocator,e.g.TCMalloc
TCMalloc.Orperhapsthecodewantstousegarbagecollectedmemoryinwhichcase
BohemGCisapopularlibraryforthatpurpose.Boehmcanalsobeusedforleakdetection
sinceitcanfindobjectswhichwereneverreleased.C++canalsooverridetheglobalor
classspecificnew/deleteoperators.SomestandardC++templateclassesalsoallow
memoryallocationtobeoverridden.
Rust
AsyoucanguessbynowRusttendstobealotmorestrictaboutallocationthatC/C++.
Lifetimesofobjectsaretrackedandenforcedbythecompilerandthatincludesmemory
allocatedobjects.
Innormalsafeprogrammingthereisnoexplicitnew/deletesothereisnowaytoforgetto
freeanobject.Therearenopointerseithersocodecannotcalladanglingpointeror
inadvertentlycallanullpointer.
1. A Boxisamanagedpointerthatholdsaheapallocatedobject.Aboxcannotbe
cloned,sothereisonlyoneowneratanytime.
2. A Cellisamutablememorylocation-itcanholdanykindofcopyabletypeandthe
valuewithinitcanbechanged.
3. A RefCellisamutablememorylocationthatcanholdareference
Theadvantageforprogrammers,isthatonceyoudefinethelifetimeofanobjectproperlyit
justcomesintoexistenceandgoesawaycorrectly.Inmanycasesthislifetimemanagement
comeswithzeroruntimecost,orifthereisacostitisnomorethanthesamecodecorrectly
writteninC/C++.
Rustrequiresmostheapallocatedmemorytobecontainedbyoneormoreofthestructs
below.Thestructmanagesthelifetimeandaccesstotheobjectinsideensuringthelifetime
ismanagedcorrectly.
Box
MemoryAllocation
170

A Boxismemorymanagedontheheap.
structBlob{
data:Box<[u8;16384]>
}
implBlob{
pubfnnew(){
Efficient{
data:Box::new([0u8;16384])
}
}
}
Whoeverownstheboxcanaccessit.Essentially,thatmeansyoucanpasstheboxaround
fromoneplacetoanotherandwhateverbindstoitlastcanopenit.Everyoneelse’sbinding
becomesinvalidandwillgenerateacompileerror.
Aboxcanbeusefulforabstractionsinceitcanrefertoastructbyatraititimplements
allowingdecouplingbetweentypes.
TODOexampleofastructholdingaboxwithatraitimplementedbyanotherstruct
Itcanbeusefulforsituationswhereonepieceofcodecreatesanobjectonbehalfofanother
pieceofcodeandhandsitover.TheBoxmakessurethattheownershipisexplicitatall
timesandwhentheboxmovestoitsnewowner,sodoesthelifetimeoftheobjectitself.
Cell
A Cellissomethingthatcancopiedwitha get()or set()tooverwriteitsowncopy.As
thecontentsmustbecopyabletheymustimplementtheCopytrait.
The Cellhasazero-costatruntimebecauseitdoesn’thavetotrackborrowsbutthe
restrictionisitonlyworksonCopytypes.Thereforeitwouldnotbesuitableforlargeobjects
ordeep-copyobjects.
RefCell
Somewhatmoreusefulisthe RefCell<T>butitincursaruntimepenaltytomaintainread-
writelocks.
MemoryAllocation
171

The RefCellholdsareferencetoanobjectthatcanbeborrowedeithermutablyor
immutably.Thesereferencesareread-writelockedsothereisaruntimecosttothissince
theborrowmustcheckifsomethingelsehasalreadyborrowedthereference.
Typicallyapieceofcodemightborrowthereferenceforascopeandthentheborrow
disappearswhenitgoesoutofscope.Ifaborrowhappensbeforethelastborrowreleases,it
willcauseapanic.
ReferenceCountingobjects
Rustimplements Rc<>and Arc<>forthepurposeofreferencecountingobjectsthatneed
tobesharedandusedbydifferentpartsofcode.Rc<>isasinglethreadedreference
countedwrapper,while Arc<>isatomicreferencecountedwrapper.Youuseoneorthe
otherdependingonwhetherthreadsaresharingtheobject.
Areferencecountedobjectisusuallywrappinga Box, Cellor Refcell.Somultiple
structscanholdareferencetothesameobject.
Rc
From std::rc::Rc.Areferencecountedobjectcanbeheldbymultipleownersatatime.
Eachownholdsacloned Rc<T>buttheTcontentsareshared.Thelastreferencetothe
objectcausesthecontentstobedestroyed.
Arc
From std::sync::Arc.Anatomicreferencecountedobjectthatworkslike Rc<T>exceptit
usesanatomicallyincrementedcounterwhichmakesitthreadsafe.Thereismoreoverhead
tomaintainanatomicreferencecount.Ifmultiplethreadsaccessthesameobjecttheyare
compelledtouse Arc<T>
MemoryAllocation
172

ForeignFunctionInterface
Rustdoesn'tworkinavaccumandwasneverintendedassuch.Insteaditwasalways
assumedthatitwouldneedtocallothercodeandothercodewouldneedtocallit,
Callotherlibrariesviatheirentrypoints
ProducelibrariesinRustthatcanbecalledbycodewritteninanotherlanguage.e.g.C,
C++,Python,Rubyetc.
TothatendithastheForeignFunctionInterface,themeanstodefineexternalfunctions,
exposeitsownfunctionswithoutnamemanglingandtoinvokeunsafecodethatwould
otherwisebeillegalinRust.
CallingouttoClibraries
Rustsupportstheconceptofaforeignfunctioninterfacewhichisadefinitionofanexternal
functionortypethatisresolvedatlinktime.
Forexample,wemightwishtolinktoalibrarycalledfoo.lib,andinvokeacommand
foo_command().
#[link(name="foo")]
extern{
fnfoo_command(command:*mutu8)
}
Tocallthisfunctionwehavetoturnoffsafetychecksfirstbecausewearesteppingoutof
theboundsofRust'slifetimeenforcement.Todothiswewrapthecallinanunsafeblockto
disablethesafetychecks:
pubfnrun_command(command:&[u8]){
unsafe{
foo_command(command.as_ptr());
}
}
Notehowwecanuseunsafefeatureslikepointersinsideofthisunsafeblock.Thisallows
interactionwiththeoutsideworldwhilestillenforcingsafetyfortherestofourcode.
ForeignFunctionInterface
173

MakingRustcodecallable
Theconverseisalsopossible.WecanproducealibraryfromRustthatcanbeinvokedby
someothercode.
Forexample,imaginewehavesomecodewritteninPython.Thecodeworksfinebutitis
notperformantandthebottleneckisinjustoneportionofthecode,e.g.somefileoperation
likeachecksum.Wewantourcodetoconsistofamake_checksum()anda
release_checksum().
externcratelibc;
usestd::ffi::CString;
usestd::ptr;
uselibc::{c_char,c_void,malloc,memset,strcpy,free};
#[no_mangle]
pubextern"C"fnmake_checksum(filepath:*constc_char)->*mut
c_char{
//Yourcodehere
iffilepath==ptr::null(){
returnptr::null_mut::<c_char>()
}
unsafe{
//Imagineourchecksumcodehere...
letresult=malloc(12);
memset(result,0,12);
strcpy(resultas*mutc_char,
CString::new("abcdef").unwrap().as_ptr());
returnresultas*mutc_char;
}
}
#[no_mangle]
pubextern"C"fnrelease_checksum(checksum:*constc_char){
unsafe{
free(checksumas*mutc_void);
}
}
ForeignFunctionInterface
174

NowinPythonwecaninvokethelibrarysimply:
importctypes
checksum=ctypes.CDLL("path/to/our/dll");
cs=checksum.make_checksum("c:/somefile");
...
checksum.release_checksum(cs)
TheFFIspecificationgoesintoalotmoredetailthanthisandexplainsconceptssuchas
callbacks,structurepacking,stdcall,linkingandotherissuesthatallowfullinteroperability.
libc
RustmaintainsacratecalledlibcwhichholdstypesandfunctionscorrespondingtoC.
Adependencytolibcwouldbeaddedtothe Cargo.tomlofyourproject:
[dependencies]
libc="0.2.17"
Andthefilethatusesthefunctionswouldcontainapreamblesuchasthissayingwhattypes
andfunctionsitcalls:
externcratelibc;
uselibc::{c_char,malloc,free,atoi};
Otherlibraries
Therearealsocratesthathavethedefinitionsofstructures,typesandfunctions.
WinAPIbindingsforWin32programmingAPIs.
OpenSSLbindingsforOpenSSL
ForeignFunctionInterface
175

FixingProblemsinC/C++
Thissectionisnotsomuchconcernedwiththecorrectwaycodeshouldbewrittenbutwhat
withtheC/C++languagesallow.Successiveversionsoftheselanguageshaveattemptedto
retrofitgoodpractice,butnotbyeliminatingthebadone!
Allofthesethingsshouldbeconsideredbadandanyconstructinthelanguagethatenables
themisalsobad:
Callingapurevirtualfunctioninaconstructor/destructorofabaseclass
Callingadanglingpointer
Freeingmemorymorethanonce
Usingdefaultcopyconstructorsorassignmentoperatorswithoutfollowingtheruleof
three
Overflowingabuffer,e.g.beingoffbyonewithsomestringoperationornottestinga
boundarycondition
Memoryleaksduetomemoryallocation/ownershipissues
Heapcorruption
TheC++programminglanguageisaverylargespecification,onethatonlygrowsandgets
morenuancedandqualifiedwitheachrelease.
Theproblemfromaprogrammer'sperspectiveisunderstandingwhatthingsC++allows
themtodoasopposetowhatthingstheyshoulddo.
Ineachcasewe'llseehowRustmighthavestoppedusgettingintothissituationinthefirst
place.
WhataboutC?
C++willcomeinformostofthecriticisminthissection.Someonemightbeinclinedtothink
thatthereforeCdoesnotsufferfromproblems.
Yesthatistruetosomeextent,butitisakintoarguingwedon'tneedshoesbecausewe
havenolegs.C++existsandispopularbecauseitisperceivedasastepupfromC.
Namespaces
Improvedtypechecking
Classesandinheritance
Exceptionhandling
Moreusefulruntimelibraryincludingcollections,managedpointers,fileioetc.
PortingfromC/C++toRust
176

Theabilitytomodelclassesandbindmethodstothemisamajoradvance.Theabilityto
writeRAIIstylecodedoesimprovethesoftware'schancesofkeepingitsmemoryand
resourceuseundercontrol.
CompilersWillCatchSomeErrors
ModernC/C++compilerscanspotsomeoftheerrorsmentionedinthissection.Butusually
they'lljustthrowawarningout.Largecodebasesalwaysgeneratewarnings,manyofwhich
areinnocuousandit'seasytoseewhysomepeoplebecomenumbtothemastheyscroll
past.
ThesimplestwaytoprotectC/C++fromdumberrorsistoelevateseriouswarningstobe
errors.Whileitisnotgoingtoprotectagainsteveryerroritisstillbetterthannothing.
InMicrosoftVC++enableahighwarninglevel,e.g./W4andpossibly/WXtowarnings
intoerrors.
InGCCenable-Wall,-pedantic-errorsandpossibly-Werrortoturnwarningsintoerrors.
Thepedanticflagrejectscodethatdoesn'tfollowISOCandC++standards.Thereare
alotoferrorsthatcanbeconfigured.
Howeverthiswillprobablythrowupalotofnoiseinyourcompilationprocessandsomeof
theseerrorsmaybebeyondyourmeanstocontrol.
Inadditionitisagoodtorunasourcecodeanalysistoolorlinter.Howeverthesetendtobe
expensiveandinmanycasescanbeextremelyunwieldy.
PortingfromC/C++toRust
177

CopyConstructor/AssignmentOperators
InC++youcanconstructoneinstancefromanotherviaaconstructorandalsobyan
assignmentoperator.Insomecasesaconstructorwillbeusedinsteadofanassignment:
PersonListx;
PersonListy=x;//Copyconstructor,notassignment
PersonListz;
z=x;//Assignmentoperator
BydefaultC++generatesallthecodetocopyandassignthebytesinoneclasstoanother
withoutanyeffort.Luckyus!
SoourclassPersonListmightlooklikethis:
structPerson{
//...
};
classPersonList{
std::vector<Person>*personList_;
public:
PersonList():personList_(newstd::vector<Person>){
}
~PersonList(){
deletepersonList_;
}
//...Methodstoadd/searchlist
};
Exceptwe'renotlucky,wejustgotslimed.Thedefaultbytecopytakesthepointerin
personList_andmakesacopyofit.Nowifwecopy xto y,orassign xto zwehave
threeclassespointingtothesameprivatedata!Ontopofthat, zallocateditsown
personList_duringitsdefaultconstructorbutthebytecopyassignmentoverwroteitwith
theonefrom xsoitsold personList_valuejustleaks.
CopyConstructor/AssignmentOperators
178

Ofcoursewemightbeabletousea std::unique_ptrtoholdourpointer.Inwhichcasethe
compilerwouldgenerateanerror.Butitmightnotalwaysbethatsimple. personList_may
havebeenopaquelyallocatedbyanexternallibrarysohavenochoicebuttomanageits
lifetimethroughtheconstructoranddestructor.
TheRuleofThree
ThisissuchaterriblebugenablingprobleminC++thatithasgivenrisetotheso-calledthe
RuleofThree .
Therulesaysthatifweexplicitlydeclareadestructor,copyconstructororcopyassignment
operatorinaC++classthenweprobablyneedtoimplementallthreeofthemtosafely
handleassignmentandconstruction.InotherwordstheburdenforfixingC++'sdefaultand
dangerousbehaviourfallsontothedeveloper.
Solet'sfixtheclass:
1
CopyConstructor/AssignmentOperators
179

structPerson{
//...
};
classPersonList{
std::vector<Person>*personList_;
public:
PersonList():personList_(newstd::vector<Person>){
}
PersonList(constPersonList&other):
personList_(newstd::vector<Person>){
personList_->insert(
personList_->end(),other.personList_->begin(),
other.personList_->end());
}
~PersonList(){
deletepersonList_;
}
PersonList&operator=(constPersonList&other){
//Don'tforgettocheckifsomeoneassignsanobjectto
itself
if(&other!=this){
personList_->clear();
personList_->insert(
personList_->end(),other.personList_-
>begin(),
other.personList_->end());
}
return*this;
}
//...Methodstoadd/searchlist
};
Whatamess!
CopyConstructor/AssignmentOperators
180

We'veaddedacopyconstructorandanassignmentoperatortotheclasstohandlecopying
safely.Thecodeevenhadtocheckifitwasbeingassignedtoitselfincasesomeonewrote
x=x.Withoutthattest,thereceivinginstancewouldclearitselfinpreparationtoadding
elementsfromitselfwhichwouldofcoursewipeoutallitscontents.
Alternativelywemightdisablecopy/assignmentsbycreatingprivateconstructorsthat
preventsthembeingcalledbyexternalcode:
classPersonList{
std::vector<Person>*personList_;
private:
PersonList(constPersonList&other){}
PersonList&operator=(constPersonList&other){return
*this;}
public:
PersonList():personList_(newstd::vector<Person>){
}
~PersonList(){
deletepersonList_;
}
//...Methodstoadd/searchlist
};
Anotheralternativewouldbetousenoncopyabletypeswithintheclassitself.Forexample,
thecopywouldfailifthepointerweremanagedwithaC++11 std::unique_ptr(orBoost's
boost::scoped_ptr).
Boostalsoprovidesa boost::noncopyableclasswhichprovidesyetanotheroption.Classes
mayinheritfromnoncopyablewhichimplementsaprivatecopyconstructorandassignment
operatorsoanycodethattriestocopywillgenerateacompileerror.
TheRuleofFive
TheRuleofThreehasbecometheRuleofFive(!)inC++11becauseoftheintroductionof
movesemantics.
CopyConstructor/AssignmentOperators
181

Ifyouhaveaclassthatcanbenefitfrommovesemantics,theRuleofFiveessentiallysays
thattheexistenceoftheuser-defineddestructor,copyconstructorandcopyassignment
operatorrequiresyoutoalsoimplementamoveconstructorandamoveassignment
operator.Soinadditiontothecodewewroteabovewemustalsowritetwomoremethods.
classPersonList{
//Seeclassaboveforothermethods,ruleofthree....
PersonList(PersonList&&other){
//TODO
}
PersonList&operator=(PersonList&&other){
if(&other!=this){
//TODO
}
return*this
}
HowRusthelps
Moveisthedefault
Rusthelpsbymakingmovesemanticsthedefault.i.e.unlessyouneedtocopydatafrom
oneinstancetoanother,youdon't.Ifyouassignastructfromonevariabletoanother,
ownershipmoveswithit.Theoldvariableismarkedinvalidbythecompileranditisanerror
toaccessit.
Butifyoudowanttocopydatafromoneinstancetoanotherthenyouhavetwochoices.
Implementthe Clonetrait.Yourstructwillhaveanexplicit clone()functionyoucan
calltomakeacopyofthedata.
Implementthe Copytrait.Yourstructwillnowimplicitlycopyonassignmentinsteadof
move.Implementing Copyalsoimpliesimplementing Clonesoyoucanstillexplicitly
call clone()ifyouprefer.
Primitivetypessuchasintegers,chars,boolsetc.implement Copysoyoucanjustassign
onetoanother
CopyConstructor/AssignmentOperators
182

//Thisisallgood
letx=8;
lety=x;
y=20;
assert_eq!(x,8);
Buta Stringcannotbecopiedthisway.Astringhasaninternalheapallocatedpointerso
copyingisamoreexpensiveoperation.So Stringonlyimplementsthe Clonetraitwhich
requiresyoutoexplicitlyduplicateit:
letcopyright="Copyright2017AcmeFactory".to_string();
letcopyright2=copyright.clone();
Thedefaultforanystructisthatitcanneitherbecopiednorcloned.
structPerson{
name:String,
age:u8
}
Thefollowingcodewillcreatea Personobject,assignsitto person1.Andwhen person1
isassignedto person2,ownershipofthedataalsomoves:
letperson1=Person{name:"Tony".to_string(),age:38u8};
letperson2=person1;
Attemptingtouse person1afterownershipmovesto person2willgenerateacompile
error:
println!("{}",person1.name);//Error,useofamovedvalue
ToillustrateconsiderthisRustwhichisequivalenttothePersonListwesawinC++
structPersonList{
pubpersons:Vec<Person>,
}
CopyConstructor/AssignmentOperators
183

Wecanseethat PersonListhasa Vecvectorof Personobjects.Underthecoversthe
Vecwillallocatespaceintheheaptostoreitsdata.
Nowlet'suseit.
letmutx=PersonList{persons:Vec::new(),};
letmuty=x;
//xisnottheowneranymore...
x.persons.push(Person{name:"Fred".to_string(),age:30u8});
Thevariable xisonthestackandisa PersonListbutthepersonsmemberispartly
allocatedfromtheheap.
Thevariable xisboundtoaPersonListonthestack.Thevectoriscreatedintheheap.If
weassign xto ythenwecouldhavetwostackobjectssharingthesamepointeronthe
heapinthesamewaywedidinC++.
ButRuststopsthatfromhappening.Whenweassign xto y,thecompilerwilldoa
bitwisecopyofthedatainx,butitwillbindownershipto y.Whenwetrytoaccessthein
theoldvarRustgeneratesacompileerror.
error[E0382]:useofmovedvalue:`*x.persons`
|
10|letmuty=x;
|-----valuemovedhere
11|x.persons.push(Person{});
|^^^^^^^^^valueusedhereaftermove
|
=note:moveoccursbecause`x`hastype`main::PersonList`,
whichdoesnotimplementthe`Copy`trait
RusthasstoppedtheproblemthatwesawinC++.Notonlystoppeditbuttolduswhyit
stoppedit-thevaluemovedfromxtoyandsowecan'tusexanymore.
ImplementingtheCopytrait
The Copytraitallowsustododirectassignmentbetweenvariables.Thetraithasno
functions,andactsasamarkerinthecodetodenotedatathatshouldbeduplicatedon
assignment.
CopyConstructor/AssignmentOperators
184

Youcanimplementthe Copytraitbyderivingit,orimplementingit.Butyoucanonlydosoif
allthemembersofthestructalsoderivethetrait:
#[derive(Copy)]
structPersonKey{
id:u32,
age:u8,
}
//Alternatively...
implCopyforPersonKey{}
implCloneforPersonKey{
fnclone(&self)->PersonKey{
*self
}
}
So PersonKeyiscopyablebecausetypes u32and u8arealsocopyableandthecompiler
willtakethe #[derive(Copy)]directiveandmodifythemove/copysemanticsforthestruct.
Butwhenastructcontainsaatypethatdoesnotimplement Copyyouwillgetacompiler
error.Sothisstruct Personwillcauseacompilererrorbecause Stringdoesnot
implement Copy:
#[derive(Copy)]
structPerson{
name:String,
age:u8
}
//Compilererror!
ImplementingtheClonetrait
The Clonetraitaddsa clone()functiontoyourstructthatproducesanindependentcopy
ofit.Wecanderiveitifeverymemberofthestructcanbeclonedwhichinthecaseof
Personitcan:
CopyConstructor/AssignmentOperators
185

#[derive(Clone)]
structPerson{
name:String,
age:u8
}
...
letx=Person{/*...*/};
lety=x.clone();
NowthatPersonderives Clone,wecandothesameforPersonListbecauseallitsmember
typesimplementthattrait-aPersoncanbecloned,aVeccanbecloned,andaBoxcanbe
cloned:
#[derive(Clone)]
structPersonList{
pubpersons:Box<Vec<Person>>,
}
Andnowwecanclone xinto yandwehavetwoindependentcopies.
//...
letmutx=PersonList{persons:Box::new(Vec::new()),};
letmuty=x.clone();
//xandyaretwoindependentlistsnow,notshared
x.persons.push(Person{name:"Fred".to_string(),age:30});
y.persons.push(Person{name:"Mary".to_string(),age:24});
Summary
Insummary,Ruststopsusfromgettingintotroublebytreatedassignsasmoveswhena
non-copyablevariableisassignedfromonetoanother.Butifwewanttobeabletoclone/
copywecanmakeourintentexplicitanddothattoo.
C++justletsusdigaholeandfillsthedirtinontopofus.
CopyConstructor/AssignmentOperators
186

MissingBracesinConditionals
Everyprogrammereventuallyencountersanerrorlikethisandspendshourstryingtofigure
outwhyitwasn'tworking.
constboolresult=fetch_files();
if(result){
process_files()
}
else
print_error()
returnfalse;
//Nowcleanupandreturnsuccess
cleanup_files();
returntrue;
Thereasonofcoursewastheelsestatementwasn'tenclosedinbracessothewrongcode
wasexecuted.Thecompilermightspotdeadcodeinthisinstancebutthatmaynotalways
bethecase.Evenifitdid,itmightonlyissueawarninginsteadofanerror.
Theproblemcanbeespeciallyannoyingindeeplynestedconditionswhereamisplaced
bracecanattachtothewronglevel.Thisproblemhasleadreal-worldsecurityissues.For
examplehereistheinfamous"gotofail"bugthatoccuredinsomeAppleproducts.This
(intentional?)bugoccuredduringanSSLhandshakeandwasexploitable.:
MissingBracesinConditionals
187

staticOSStatus
SSLVerifySignedServerKeyExchange(
SSLContext*ctx,boolisRsa,SSLBuffersignedParams,
uint8_t*signature,UInt16signatureLen)
{
OSStatuserr;
//...
if((err=SSLHashSHA1.update(&hashCtx,&serverRandom))!=0)
gotofail;
if((err=SSLHashSHA1.update(&hashCtx,&signedParams))!=0)
gotofail;
gotofail;
if((err=SSLHashSHA1.final(&hashCtx,&hashOut))!=0)
gotofail;
//...
fail:
SSLFreeBuffer(&signedHashes);
SSLFreeBuffer(&hashCtx);
returnerr;
}
Notehowthe"gotofail"isrepeatedtwiceandnotboundtotheconditionbutisindentedasif
itwas.Thecodewouldjumpstraightintothefaillabelandreturnwithanerrindicating
success(sincethepriorSHA1updatehadsucceeded).Ifconditionals
HowRusthelps
Rustrequiresif-elseexpressionsandloopstobeassociatedwithblocks.
Sothiscodewon'tcompile:
MissingBracesinConditionals
188

letmutx:i32=do_something();
ifx==200{
//...
}
else
println!("Error");
Ifyoutryyouwillgetanerrorlikethis.
rustc1.13.0-beta.1(cbbeba4302016-09-28)
error:expected`{`,found`println`
|
8|println!("Error");
|^^^^^^^
|
help:tryplacingthiscodeinsideablock
|
8|println!("Error");
|^^^^^^^^^^^^^^^^^^
error[E0425]:unresolvedname`do_something`
|
3|letmutx:i32=do_something();
|^^^^^^^^^^^^unresolvedname
MissingBracesinConditionals
189

AssignmentinConditionals
Theomissionofan =inan ==conditionturnsitintoanassignmentthatevaluatestotrue:
intresult=getResponseCode();
if(result=200){//BUG!
//Success
}
else{
//...Processerror
}
Sohere,resultwasassignedthevalue200ratherthancomparedtothevalue200.
Compilersshouldissueawarningforthesecases,butanerrorwouldbebetter.
Developersmightalsotrytoreversetheleftandrighthandsidetomitigatetheissue:
if(200=result){//Compilererror
//Success
}
else{
//...Processerror
}
Nowthecompilerwillcomplainbecausethevalueofresultisbeingassignedtoaconstant
whichmakesnosense.Thismayworkifavariableiscomparedtoaconstantbutarguablyit
makesthecodelessreadableandwouldn'thelpiftheleftandrighthandsideswereboth
assignablesotheirorderdidn'tmatter.
The gotofailexamplethatwesawinsection"Missingbracesinconditionals"also
demonstratesarealworlddangerscombiningassignmentandcomparisonintoasingleline:
if((err=SSLHashSHA1.update(&hashCtx,&serverRandom))!=0)
gotofail;
Thislineisnotbrokenforotherreasons,butit'seasytoseehowmightbe,especiallyifthis
patternwererepeatedallovertheplace.Theprogrammermighthavesavedafewlinesof
codetocombineeverythinginthiswaybutatagreaterrisk.Inthiscase,theriskmightbe
AssignmentinConditionals
190

inadvertantlyturningthe =intoan ==,i.e.comparingerrtothefunctioncallandthen
comparingthatto0.
if((err==SSLHashSHA1.update(&hashCtx,&serverRandom))!=0)
gotofail;
HowRusthelps
Thiscodejustwon'tcompile:
letmutresult=0;
ifresult=200{//CompileError
//...
}
Theonlyformofassignmentinsideaconditionalisthespecialisedandexplicit ifletand
whileletformswhichareexplainedelsewhere.
AssignmentinConditionals
191

ClassMemberInitialisation
C++doesnotrequirethatyouinitialiseallvariablesineveryconstructor.
AmemberthatisaC++classwithitsowndefaultconstructordoesn'tneedtobe
initialised
AmemberthatisaC++classwithoutadefaultconstructormustbeexplicitlyinitialised.
Amemberthatisareferencemustbeexplicitlyinitialised
Primitivetypes,includingpointersdonothavetobeinitialisedalthoughthecompiler
maywarniftheyarenot
Membersdonothavetobeinitialisedintheordertheyaredeclared
Somecompilersmayissuewarningsifyouforgettoinitialisemembersortheirordering,but
theywillstillcompilethecode.
C++11allowsclassestohavedefaultmemberinitializerswhichareusedintheabsenceofa
constructorsettingthevaluetosomethingelse:
classCoords{
public:
doublex=0.0;
doubley=0.0;
doublez=0.0;
//2Dinitializer,xandyaresetwiththeinputs,zisset
to0
Coords(doublex,doubley):x(x),y(y){}
};
Thisisobviouslyaloteasiertoreadandensuresthatifwehavemultipleconstructorsthat
wedon'thavetoinitializemembersifthedefaultvaluewilldo.
HowRusthelps
YouMUSTinitialiseallmembersofastruct.Ifyourcodedoesnotinitialiseastructyouwill
getacompilererror.
Thiswillnotcompile:
ClassMemberInitialisation
192

structAlphabet{
a:i32,
b:u32,
c:bool,
}
leta=Alphabet{a:-10,c:true};
Ifyoutryyouwillgetanerrorlikethis:
rustc1.13.0-beta.1(cbbeba4302016-09-28)
error[E0063]:missingfield`b`ininitializerof
`main::Alphabet`
|
9|leta=Alphabet{a:-10,c:true};
|^^^^^^^^missing`b`
Forcingyoutoinitialisethemembersofthestructensuresthestructisalwaysina
consistentpredictablestate.
Orderingofinitialisationdoesnotmatterprovidingallofthefieldsareset.
Structsoftenimplementa new()functionwhichencapsulatesthisinitialisationandactslike
aconstructorinC++,e.g.
structCoord{
pubx:f64,
puby:f64,
pubz:f64,
}
implCoord{
pubfnnew(x:f64,y:f64){
Coord{x:x,y:y,z:0f64}
}
}
///...
letcoord1=Coord::new(100f64,200f64);
ClassMemberInitialisation
193

Alternativelythestructmightimplementoneormore From<>traits:
implFrom<(f64,f64)>forCoord{
fnfrom(value:(f64,f64))->Coord{
Coord{x:value.0,y:value.1,z:0.0}
}
}
implFrom<(f64,f64,f64)>forCoord{
fnfrom(value:(f64,f64,f64))->Coord{
Coord{x:value.0,y:value.1,z:value.2}
}
}
//...
letcoord=Coord::from((10.0,20.0));
letcoord=Coord::from((10.0,20.0,30.0));
Therecanbemultiple Fromtraitimplementationssowecanimplementaformof
polymorphism.
ClassMemberInitialisation
194

HeadersandSources
Aheaderfilecontainsdefinitionsofclasses,types,macrosetcthatotherfilesneedto
#includeinordertoresolvetheiruseofthosethings.
Splittingtheimplementationanddefinitionacrossdifferentfilesisanaddedburdenfor
maintainingcodebutitcanalsoleadtosomeseriouserrors.
Headersusedacrossmultipleprojectsthathavedifferentcompilersettings
Issueswithpragmasandalignment
Issueswithdifferent#definitionsthataffectbytelength
Issueswithdifferenttypedefsthataffectbytelength
Eachconsumeroftheheadermustdosowiththeexactsamesettingsthataffectthesizeof
everytype,structandclassinthefileplusanyissueswithpacking/alignment.Ifthese
settingsarenotthesame,itcancauseinstability,corruptionorproblemsthatonlymanifest
themselvesatatruntime.
Headersalsomakethecompilerslowerbecausesourcethatconsumestheheader
inevitablypullsinotherheaderswhichpullinotherheaders.
Guardblocks/#pragmaonce
Headerswillalsobeexpandedasmanytimesastheyare #include'd.Topreventthe
expansionhappeningmorethanoncepersourcefile,they'reusuallyprotectedbyguard
blocks.
#ifndefFOO_H
#defineFOO_H
....
#endif
Ifthesameheaderisincludedmorethanonce,thesecondtimethroughitispreprocessed
intonothing.
#pragmaonce
HeadersandSources
195

Mostmoderncompilersalsosupporta #pragmaoncedirective.Thisallowsthecompilerto
completelyignorean #includewhichitknowsithasalreadyincludedatleastoncebefore
persourcefile.
Thisismoreefficientthanguardblocksbecausethecompiledoesn'tevenbotheropeningor
processingthefileagainandjustskipsoverit.Theremaybesituationswherethisisnot
suitable,butusuallyitresultsinfastercompilation.
PrecompiledHeaders
Somecompilersalsosupportprecompiledheaderstospeedupcompilation.Thecompiler
buildsadatabaselookupwhencompilingasinglesourcefileandsubsequentsource
compileswithreferencetothatdatabase.Thissolutioncanspeedupcompilationbutit
complicatesthebuildprocesssinceonefilehasflagstogeneratetheprecompiledheader
fileandothersourceshaveflagstoreferenceit.
Pimplpattern
ApopularworkaroundforheaderissuesisthePimplpattern.Itisawaytoseparateaclass
intoapublicpartandaprivateimplementationpart.
Thepublicclassisalmostaninterfacedefinitioninitspuritythatcanbedefinedinthe
headerwithminimaldependencies.Itforwardreferencestheimplementationclassand
storesitasamember:
#pragmaonce
//Gorydetailsareinthe.cppfile
classComplexThingImpl;
classComplexThing{
ComplexThingImpl*pimpl_;
public:
ComplexThing();
~ComplexThing();
//Seenote1below
voidsomethingReallyComplex();
};
HeadersandSources
196

Theconstructorfortheouterclasswouldallocatetheimplementationclassandmethodcalls
wouldcallthroughtotheinner.
Theprivateimplementationclassisdefinedinthesourcefileandcanpullinasmanyextra
headersasitneeds,pragmaswhateverwithouthurtingconsumersorcompiletimesofthe
header.
//sourcefile
#include"random/header.hpp"
//Lotsofincludeshere
#include<...>
#include"more/stuff.hpp"
classComplexThingImpl{
//Lotsofmembervariablesandstuffhere
//...
public:
voidsomethingReallyComplex();
}
voidComplexThingImpl::somethingReallyComplex(){
//Lotsofcomplexstuffhere
//...
}
ComplexThing::ComplexThing():
pimpl_(newComplexThingImpl()){
}
ComplexThing::~ComplexThing(){
deletepimpl_;
}
voidComplexThing::somethingReallyComplex(){
pimpl_->somethingReallyComplex();
}
ThissolutionisknownasPimpl(privateimplementation)patternandwhileitcanworkto
protectconsumersandspeedupbuildsitalsoaddscomplexityandoverheadto
development.Insteadof2definitionsofaclasstomaintain(header/source)younowhave
HeadersandSources
197

4(!)becausethereisapublicandprivateimplclass.Changingthesignatureofamethod
meanschangingitinpotentially4places,plusthelineinthepublicclassthatinvokesthe
privatecounterpart.
OnedangerforPimplisthattheprivateclassisallocatedfromtheheap.Codethatusesa
lotoftemporaryPimplobjectscouldcontributetoheapfragmentation.
Note1:Remembertheruleofthree?Thatappliestothisobjecttoo.Theexampledoesn't
showitbutifwecopyconstructedorassignedComplexThingtoanotherinstancewe'dbein
aheapoftrouble.SoontopoftheissueswithmakingPImplworkwealsohavetoprevent
theotherones.Theeasiestwaytolockitdownwouldbetoderivefrom boost::noncopyable
ifyouwereusingboostormakethecopyconstructor private,orusedeleteitinC++11.
HowRusthelps
InRustthedefinitionandtheimplementationarethesamething.Soimmediatelywehave
exactlyonethingtomaintain.
Writingafunctiondefinesthefunction.Let'sassumewehaveafunctions.rsfile
//functions.rs
pubfncreate_directory_structure(){
//Implementation
}
Anyonecancallitas functions::create_directory_structure().Thecompilerwillvalidate
thecalliscorrect.
Astruct'sdefinitionanditsimplementationarealsowrittenonce.e.g. directory.rs
//directory.rs
pubstructDirectory{
pubpath:String,
}
implDirectory{
pubfnmkdir(&self){
//implementation
}
}
HeadersandSources
198

ImplementationscanbedefinedinaprivateRustmoduleandonlypublicstructsexposedto
consumers.
Ifwewerealibrarycrate(whichwe'llcall file_utils)wishingtoexposetheseobjectsto
consumerswewouldwriteatop-level lib.rswhichsayswhatfilesourlibcomprisesof
andwewanttoexpose.
//lib.rsforfile_utils
modfunctions;
moddirectory;
pubusefunctions::*;
pubusedirectory::Directory;
Nowaconsumercanuseourcrateeasily:
externcratefile_utils;
usefile_utils::*;
fnmain(){
create_directory_structure();
letd=Directory{/*...*/};
}
HeadersandSources
199

ForwardDeclarations
C++preventsusfromreferringtoaclassorfunctionwhichhasnotbeendefinedyet.The
compilerwillcomplaineveniftheclassorfunctionisinthesamefileitisreferencedfrom.
Thismeansorderingmatters.Ifourfunctionorclassisusedbyotherfiles,wehaveto
declarethefunctioninaheader.Ifourfunctionisprivatetoasourcefile,wehavetodeclare
itinthesourcefile,andpossiblymakeitstatic.
Forclasseswecanmakeaforwardreference.Thisactsasahinttocompilertosayaclass
doesexistwiththisnameanditwillbetoldaboutitshortly.Butit'sahackanditimposes
limitsonhowwecanusetheforwarddeclaredclass.
Forexample,DataManagerbelowcanhandoutDataobjectsbuttheDataobjecthasa
referencetotheDataManager.Sinceeachclassreferstoeachotherthereisnosimpleway
tomakethecompilerhappyexceptwithaforwarddeclaration.
classData;//Forwarddeclaration
classDataManager{
public:
Data*getDataById(conststd::string&id);
};
classData{
public:
Data(DataManager&dataManager);
}
Butforwarddeclarationcompromisesthedesignofthecode.Forexamplewecouldn'thold
theDataobjectsinacollectionclass:
classData;
classDataManager{
std::map<std::string,Data>data_;
public:
Data*getDataById(conststd::string&id);
}
ForwardDeclarations
200

Thecompilerwouldcomplainbecauseitdoesn'tknowanythingabouttheconstructorsor
sizeofData.Soinstantlythedesignhastochangebecauseofadumbcompilerrestriction.
e.g.wemightstoreapointertoDatainsteadinthemapbutthenwe'dhavetorememberto
deleteit.Soforwardreferencesincreasethepotentialforbugs.
classData;
classDataManager{
//Great,nowwehavetoremembertonew/deleteDataandwe
increase
//memoryfragmentation
std::map<std::string,Data*>data_;
public:
Data*getDataById(conststd::string&id);
}
HowRusthelps
InRustforwarddeclarationsareunnecessary.Thestructandfunction’sdefinitionresideina
.rsandcanbereferencedwithausedirective.
ForwardDeclarations
201

NamespaceCollisions
CcodehasnonamespacesatallandnamespacesinC++areoptional.
Chaslearnedtolivewithoutnamespaces.MostCcodeusesprefixesonfunctionsand
structstoavoidcollisions,e.g sqlite3_exec()isafunctionbelongingtoSQLite3.The
prefixstopsthefunctioncollidingwith exec()whichisastandardPOSIXfunctionthat
gottherefirst.Sotheprefixactsasapseudonamespace.Butitaddsnoisetoourcode
andwouldnotbenecessaryifnamespacesweresupportedandenforced.
C++makesthemeasytodeclarebutthereisnocompunctionforanycodetobotheror
todosoinanythingbutthemostperfunctoryway.
Macrosarenotaffectedbynamespaces.Forexample,if TRUEand FALSEaredefined
bysomeheadertheytainteverythingthat #include'sthosedefinitions.
BydefaultallC++coderesidesinaglobalnamespace:
voidhello(){
//Myfunctionhelloisintheglobalnamespace,i.e.
::hello()
}
intmain(){
//Mainentrypoint
hello();
}
Thefunction hello()ispartoftheglobalnamespace.Thecalltoitwithin maincouldbe
replacedwithcallsto ::hello().Theproblemofcourseisthatthemorecodewewriteinto
theglobalnamespace,orthemorelibrarieswepullinthathavenonamespaces,themore
chancethereisofcollisions.
Namespacingrequirescodeenclosethenamespacedportioninablock.
namespaceapplication{
//stuffinherebelongstoapplication::
}
//...
application::Appapp("myapp");
NamespaceCollisions
202

Itisalsoeasytoabusenamespaces,forexamplethishappenssometimesandisNOTa
goodidea:
//Insideoffoo.h...
usingnamespacestd;
//...allcodeafterhereistaintedwithstd
Anyfilethatsays #include"foo.h"willinadvertentlytellthecompilertoautomaticallylook
upunscopedtypesandfunctionsagainststdwhichmaynotbewhatthecodewantsatall.
Nestednamespacingisalsopossiblebutitcanlookmessy.
namespaceapplication{namespacegui{
//stuffinherebelongstoapplication::gui::
}}
//...eg.
application::gui::Point2dpoint(100,100);
IfweforgettocloseabracewhennestingheadersitbecomesveryeasytomakeC++throw
upawallofincoherenterrors.
HowRusthelps
InRusteveryfileisimplicitlyamodule(equivalenttoanamespace).YoucannotNOTuse
modulesbecauseyougetthemautomatically.
Ifyouhaveacollisionbetweenthenamesofcratesormodulesy
NamespaceCollisions
203

Macros
MacrosinC/C++arebasicallylittlerulesthataredefinedbyapreprocessorandsubstituted
intothecodethatthecompilerultimatelyattemptstocompile.
Moderncodingpracticethesedaysistouseinlinefunctionsandconstantsinsteadof
macros.
Buttherealityistheycanstillbe(ab)usedandcodeoftendoes.Forexamplecodemight
insertdebugstatementsorloggingwhichiscompiledawayinreleasemode.
AnothercommonuseisonWindowswherethetype TCHARcompilestobeeither charor
wchar_tdependingon #defineUNICODEbeingpresentornot.Alongwithitgomacroslike
USES_CONVERSION, A2CT, T2CWetc.Codeshouldcompilecleanlyeitherwaybutthereality
isusuallyitdoesn't.
Aclassicproblemwouldbesomethinglikethis:
#defineSQUARED(x)x*x
//Andincode
floatresult=SQUARED(++x);
Thatwouldexpandto
floatresult=++x*++x;
Sothevalueinresultwouldbewrongandthevalueinxwouldbeincrementedtwice.
Compilationerrors
Considerwearecompilingthisstructure:
//Header
structTooltip
#ifTOOLTIP_VERSION>4
charbuffer[128];
#else
charbuffer[64];
#endif
};
Macros
204

AndinC++
Tooltiptooltip;
memset(&tooltip,0,sizeof(tooltip));
Ifwefailtodefine TOOLTIP_VERSIONtothesamevalueintheimplementationasinthecaller,
thenthiscodemaystompallovermemorybecauseitthinksthestructis128bytesinone
placeand64bytesinanother.
Namespaceissues
Macrosaren'tnamespacedandinsomecasesthisleadstoproblemswhereamacro
definitioncollideswithawellqualifiedsymbol.Forexamplecodethat #include<windows.h>
getsa #defineTRUE1.Butthatexcludesanyothercodethatexpectstocompileon
Windowsfromeverusing TRUEasaconstnomatterhowwelltheyqualifyit.Consequently
codehastodoworkaroundssuchas #undefmacrostomakecodeworkorusinganother
value.
#ifdefTRUE
#defineTMP_TRUETRUE
#undefTRUE
#endif
boolvalue=myapp::TRUE;
#ifdefTMP_TRUE
#defineTRUETMP_TRUE
#undefTMP_TRUE
#endif
Ugh.Butmorelikelywe'llrenamemyapp::TRUEtosomethinglikemyapp::MYAPP_TRUEto
avoidtheconflict.It'sstillanuglyworkaroundforaproblemcausedbyinconsiderateuseof
macros.
CommonlyusedwordslikeTRUE,FALSE,ERROR,OK,SUCCESS,FAILaremoreorless
unusablethankstomacros.
HowRusthelps
Macros
205

Rustprovidesdeveloperswithconsts,inlineattributes,andplatform/architectureattributes
forthepurposeofconditionalcompilation.
Rustoffersmacrosbuttheyconsistofasetofmatchingrulesthanmustgenerate
syntacticallyRust.Macroexpansionisperformedbythecompilersoitiscapableof
generatingerrorsonthemacroifthemacroisinerror.
Macros
206

TypeMismatching
Considertwomethods.Botharecalledevaluate()andtheyareoverloaded.Themain()
methodcallsevaluate("Helloworld").Whatversioniscalledinthecompiledcode?
#include<iostream>
#include<string>
usingnamespacestd;
voidevaluate(boolvalue){
cout<<"Evaluatingabool"<<value<<endl;
}
voidevaluate(conststd::string&value){
cout<<"Evaluatingastring"<<value<<endl;
}
intmain(){
evaluate("Helloworld");
return0;
}
Itmaysurpriseyoutoknowthattheboolversioniscalledandthecompilerdoesn'teven
complainaboutiteither:
Evaluatingabool1
Thisisanexampleofbadtypeinference.Astringliteral(achar)shouldbeturnedintoa
std::string(aC++stringhasaconstructorthattakeschar)butthecompilerchosetotreatit
asaboolinstead.
Onotheroccasionsthecompilermightspotambiguityandcomplainbuttheblurredlines
betweentypesinC++combinedwithoverloadingleadtoerrors:Hereisanotherexample
wherethecompilerisalittlemoreusefulbygeneratinganerror,butindoingsoit
demonstratesthelimitsofoverloading
TypeMismatching
207

boolevaluate(boolvalue);
boolevaluate(doublevalue);
Theseoverloadedmethodsshouldbedistinctbutthey'renotdistinctenoughasfarasthe
compilerisconcerned.
Insummary,blurredandconfusingrulesabouttypesinC++cancauseunexpectederrors
thatcanpropagatetoruntime.
HowRusthelps
InRustthefunctionscannotbeoverloadedinthismanner.
Rustisalsomorestrictabouttypecoercion-ifyouhaveaboolyoucannotpassittoa
functionthattakesaninteger.
Norcanyoupassanintegerofonesizetoafunctiontakinganintegerofanothersize.
fnprint_i32(value:i32){
println!("Yourvalueis{}",value);
}
letvalue=20i16;//16-bitint
print_i32(value);
Thiswillyieldanerror:
error[E0308]:mismatchedtypes
|
7|print_i32(value);
|^^^^^expectedi32,foundi16
Youmustuseanexplicitnumericcasttoturnthevalueintothetypethefunctionexpects:
print_i32(valueasi32);
TypeMismatching
208

Explicit/ImplicitClassConstructors
It'snotjustoverloadingthatcanbeamess.C++hasabunchofrulesaboutimplicit/explicit
typeconversionforsingleargumentconstructors.
Forexample:
classMagicNumber{
public:
MagicNumber(intvalue){}
};
voidmagic(constMagicNumber&m){
//...
}
intmain(){
//...
magic(2016);
return0;
}
Thefunction magic()takesa constMagicNumber&yetwecalleditwith 2016anditstill
compiled.Howdiditdothat?Wellour MagicNumberclasshasaconstructorthattakesan
intsothecompilerimplicitlycalledthatconstructorandusedthe MagicNumberityielded.
Ifwedidn'twanttheimplicitconversion(e.g.maybeit'shorriblyexpensivetodothiswithout
knowing),thenwe'dhavetotackan explicitkeywordtotheconstructortonegatethe
behaviour.
explicitMagicNumber(intvalue){}
Itdemonstratesaninstancewherethedefaultbehaviorisprobablywrong.Thedefault
shouldbe explicitandifprogrammerswantimplicittheyshouldberequiredtosayit.
C++11addstotheconfusionbyallowingclassestodeclaredeletedconstructorswhichare
anti-constructorsthatgenerateanerrorinsteadofcodeiftheymatch.Forexample,perhaps
weonlywantimplicit intconstructorstomatchbutwewanttostopsomebodypassingina
double.Inthatcasewecanmakeaconstructorfor doubleandthendeleteit.
Explicit/ImplicitClassConstructors
209

classMagicNumber{
public:
MagicNumber(intvalue){}
MagicNumber(doublevalue)=delete;
};
voidmagic(constMagicNumber&m){
//...
}
//...
magic(2016);//OK
magic(2016.0);//error:useofdeletedfunction
'MagicNumber::MagicNumber(double)'
HowRusthelps
Rustdoesnothaveconstructorsandsothereisnoimplicitconversionduringconstruction.
AndsincethereisnoimplicitconversionthereisnoreasontohaveC++11stylefunction
deleteoperatorseither.
Youmustwriteexplicitwrite"constructor"functionsandcallthemexplicitly.Ifyouwantto
overloadthefunctionyoucanuse Into<>patternstoachieveit.
Forexamplewemightwriteour MagicNumberconstructorlikethis:
structMagicNumber{/*...*/}
implMagicNumber{
fnnew<T>(value:T)->MagicNumberwhereT:Into<MagicNumber>
{
value.into()
}
}
Wehavesaidherethatthe new()functiontakesasitsargumentanythingthattype T
whichimplementsthetrait Into<MagicNumber>.
Sowecouldimplementitfor i32:
Explicit/ImplicitClassConstructors
210

implInto<MagicNumber>fori32{
fninto(self){
MagicNumber{/*...*/}
}
}
Nowourclientcodecanjustcall newandprovidingitprovidesatypewhichimplements
thattraitourconstructorwillwork:
letmagic=MagicNumber::new(2016);
//Butthiswon'tworkbecausef64doesn'timplementthe
trait
letmagic=MagicNumber::new(2016.0);
Explicit/ImplicitClassConstructors
211

PoorLifetimeEnforcement
Afunctionlikeiscompletelylegalanddangerous:
std::string&getValue(){
std::stringvalue("Helloworld");
returnvalue;
}
Thisfunctionreturnsareferencetoatemporaryvariable.Whoevercallsitwillgeta
referencetogarbageonthestack.Evenifitappearstowork(e.g.ifwecalledthereference
immediately)itisonlythroughluckthatitdoes.
Ourcompilerwillprobablyissueawarningforthistrivialexamplebutitwon'tstopusfrom
compilingit.
HowRusthelps
Rusttracksthelifetimeofallobjectsandknowswhentheirlifetimebeginsandends.It
tracksreferencestotheobject,knowswhenitisbeingborrowed(beingpassedtoafunction
/scope).
Itgenerateacompilererrorifitdetectsanyviolationsofitslifetime/borrowingrules.Sothe
abovecodewouldfailtocompile.
PoorLifetimeEnforcement
212

MemoryAllocation
Allocatedmemoryismemorythatisrequestedfromaportionofmemorycalledaheap,used
forsomepurposeandreturnedtothefreespacewhenitisnolongerrequired.
InCmemoryisallocatedandfreedthrougharelativelysimpleAPI:
mallocand callocallocatememoryand freedestroysit.
HoweverC++alsoneedsallocatesthatcalltheappropriateconstructorsanddestructorsso
inadditiontoC'smemoryallocationfunctions,therearekeywordsforallocation/free.
new/ deleteforC++classinstances
new[]and delete[]forarraysofclasses
Theabovebutthroughscoped/sharedpointerclassesthattakeownershipofthe
pointerandfreeitwhenappropriate.
Ifwefailtofree/deletememorythatwe'veallocated,theprogramwillleakmemory.Ifwe
free/deletememorywe'vealreadydeallocated,theprogrammaycrash.IfwefreeaC++
classwithaC free()theprogrammayleakmemorybecauseanymembervariableswill
notbedestroyedproperly.Ifwefailtocallthecorrectconstructoranddestructorpairthe
programmayleak/crash.
Acottageindustryoftoolshassprungupjusttotryanddebugissueswithmemoryleaks,
crashesandsoforth.ToolslikeValgrindetc.specialiseintryingtofigureoutwhoallocated
somethingwithoutfreeingit.
Forexample,what'swrongwiththis?
std::string*strings=newstd::string[100];
//...
deletestrings;
Oopsweallocatedanarrayofstringswith new[]butcalled deleteinsteadof delete[].
Soinsteadofdeletinganarrayofstringswecalleddeleteonthefirstmember.99ofthose
string'sdestructorswillneverbecalled.Weshouldhavewritten:
delete[]strings;
Butthecompilerdoesn'tcareandsowehavecreatedapotentiallyhard-to-findbug.
MemoryAllocation
213

Someoftheproblemswithmemoryallocationcanbemitigatedbywrappingpointerswith
scopedorsharedpointerclasses.Butthereareevenproblemswhichcanpreventthemfrom
working.
It'snotagoodideatoallowmemoryallocationtocrossalibraryboundary.Somanylibraries
providenew/freefunctionsthroughtheirAPI.Issuesaboutbalancingcallsapplytothem
too.
HowRusthelps
DuringnormalsafeprogrammingRusthasnoexplicitmemoryallocationordeallocation.We
simplydeclareanobjectanditcontinuestoexistuntilitslifetimegoesoutofscope(i.e.
nothingreferstoitanymore).
ThisisNOTgarbagecollection.Thecompilertracksthelifetimeoftheobjectandgenerates
codetoautomaticallydeleteitatthepointitisnolongerused.Thecompileralsoknowsifwe
encloseanobject'sdeclarationinsideacell,box,rcorsimilarconstructthattheobject
shouldbeallocatedontheheapandotherwiseitshouldgoonthestack.
Allocation/deallocationisonlyavailableinunsafeprogramming.Wewouldnotonly
ordinarilydothisexceptwhenweareinteractingwithanexternallibraryorfunctioncalland
explicitlytagthesectionasunsafe.
MemoryAllocation
214

VirtualDestructors
C++allowsclassestoinheritfromotherclasses.
Insomecases,suchasthisexample,thiscanleadtomemoryleaks:
classABase{
public:
~ABase(){}
};
classA:publicABase{
std::string*value_;
public:
A():value_(newstd::string){}
~A(){deletevalue_;}
};
voiddo_something(){
ABase*instance=newA();
//...
deleteinstance;
}
SohereweallocateapointertoA,assignitto"instance"whichisoftype ABase,do
somethingwithitandfinallydeleteit.Itlooksfinebutwejustleakedmemory!Whenwe
called"deleteinstance"thecodeinvokedthedestructor ~ABase()andNOTthedestructor
~A().And value_wasnotdeletedandthememoryleaked.Evenifwe'dusedascoped
pointertowrap value_itwouldstillhaveleaked.
Thecodeshouldhavesaid
classABase{
public:
virtual~ABase(){}
};
VirtualDestructors
216

Thecompilerdidn'tcareourcodewasinerror.Itjustallowedustoleakforthesakeofa
missingkeyword.
HowRusthelps
RustalsodoesnotuseinheritancesoproblemslikeABaseabovecannotexist.InRust
ABasewouldbedeclaredasatraitthatAimplements.
traitABase{
//...
}
structA{
value:String,
}
implABaseforA{
//...
}
Rustalsoallowsourstructtoimplementanothertraitcalled Dropwhichisequivalenttoa
C++destructor.
implDropforA{
fndrop(&mutself){
println!("Ahasbeendropped!");
}
}
Itallowsourcodetodosomethingduringdestructionsuchastofreeanopenresource,log
amessageorwhatever.
VirtualDestructors
217

ExceptionHandling/Safety
TherearenohardandfastrulesforwhenafunctioninC++shouldthrowanexceptionand
whenitshouldreturnacode.Soonecodebasemayhaveatendencytothrowlotsof
exceptionswhileanothermightthrownoneatall.
Asidefromthat,codemayormaynotbeexceptionsafe.Thatis,itmayormaynotfreeup
itsresourcesifitsuffersanexception.
Articleshavebeenwrittentodescribethelevelsofguaranteesthatcodecanaimforwith
exceptionsafety.
Constructors
Youmayalsobeadvisedtothrowexceptionsinconstructorsbecausethereisnoeasyway
tosignaltheobjectisanerrorotherwiseexcepttosetthenewobjectintosomekindof
zombie/deadstateviaaflagthathastobetested.
DatabaseConn::DatabaseConn(){
db_=connect();
if(db_==NULL){
throwstring("Thedatabaseconnectionisnull");
}
}
//Thesebothrecovertheirmemory
DatabaseConndb1;
DatabaseConn*db2=newDatabaseConn();
ButifDatabaseConn()hadallocatedsomememorybeforethrowinganexception,thiswould
NOTberecoveredandso~DatabaseConnwouldhavetocleanitup.
ExceptionHandling/Safety
218

DatabaseConn::DatabaseConn(){
buffer_=newchar[100];
//...exceptionthrowingcode
}
DatabaseConn::~DatabaseConn(){
if(buffer_){
delete[]buffer_;
}
}
Butifwewaiteduntilaftertheexceptionthrowingtoallocatememorythenmaybebuffer_is
notsettoNULL,sowe'dhavetoensureweinitialisedittoNULL.
DatabaseConn::DatabaseConn():buffer_(NULL){
//...exceptionthrowingcode
buffer_=newchar[100];
}
Destructors
ButyouwillbeadvisedNOTtothrowexceptionsindestructorsbecausethrowingan
exceptionduringastackunwindfromhandlinganotherexceptionisfatal.
BadNews::~BadNews(){
if(ptr==NULL){
throwstring("Thisisabadidea");
}
}
HowRusthelps
Therecommendedwayofdealingwitherrorsistousethe Optionand Resulttypesto
formallypasserrorstoyourcaller.
ExceptionHandling/Safety
219

Forirregularerrorsyourcodecanchoosetoinvoke panic!()whichisalittlelikean
exceptioninthatitwillcausetheentirethreadtounwind.Ifthemainthreadpanicsthenthe
processterminates.
A panic!()canbecaughtandrecoveredfrominsomescenariosbutitisthenuclear
option.
LackingexceptionsmightseemabadideabutC++demonstratesthattheycomewitha
wholeraftofconsiderationsoftheirown.
ExceptionHandling/Safety
220

TemplatesvsGenerics
What'satemplate?
C++providesawayofsubstitutingtypesandvaluesintoinlineclassesandfunctionscalled
templates.Thinkofitasasophisicatedsubstitutionmacro-youspecifyatypeTinthe
templateandthiscansubstituteforatype intorsomethingelseatcompiletime.During
compilationyou'llbetoldifthereareanyerrorswiththetypeyousupply.Thisisavery
powerfulfeaturesinceitallowsaclasstobereusedformanydifferenttypes.
TemplatesareusedextensivelyintheC++library,Boostandinotherplaces.Collections,
strings,algorithmsandvariousotherpieceofcodeusetemplatesinoneformoranother.
However,templatesonlyexpandintocodewhensomethingactuallycallstheinlinefunction.
Then,ifthetemplatecallsothertemplates,theinlinecodeisexpandedagainandagainuntil
thereisalargebodyofcodewhichcanbecompiled.Asmallerrorinourcodecan
propogateintoanenormouswallofnoiseinthemiddleofsomeexpandedtemplate.
Forexampleavectortakesatypeitholdsasatemplateparameter.Sowecancreatea
vectorofPatientRecords.
classPatientRecord{
std::stringname_;
PatientRecord(){}
PatientRecordoperator=(constPatientRecord&other){return
*this;}
public:
PatientRecord(conststd::string&name):name_(name){
}
};
...
std::vector<PatientRecord>records;
Sofarsogood.Solet'saddarecord:
records.push_back(PatientRecord("JohnDoe"));
TemplatesvsGenerics
221

Thatworkstoo!Nowlet'strytoerasetherecordwejustadded:
records.erase(records.begin());
Boom!
c:/mingw/i686-w64-mingw32/include/c++/bits/stl_algobase.h:In
instantiationof'static_OIstd::__copy_move<true,false,
std::random_access_iterator_tag>::__copy_m(_II,_II,_OI)[with
_II=PatientRecord*;_OI=PatientRecord*]':
c:/mingw/i686-w64-
mingw32/include/c++/bits/stl_algobase.h:396:70:requiredfrom
'_OIstd::__copy_move_a(_II,_II,_OI)[withbool_IsMove=
true;_II=PatientRecord*;_OI=PatientRecord*]'
c:/mingw/i686-w64-
mingw32/include/c++/bits/stl_algobase.h:434:38:requiredfrom
'_OIstd::__copy_move_a2(_II,_II,_OI)[withbool_IsMove=
true;_II=__gnu_cxx::__normal_iterator<PatientRecord*,
std::vector<PatientRecord>>;_OI=
__gnu_cxx::__normal_iterator<PatientRecord*,
std::vector<PatientRecord>>]'
c:/mingw/i686-w64-
mingw32/include/c++/bits/stl_algobase.h:498:47:requiredfrom
'_OIstd::move(_II,_II,_OI)[with_II=
__gnu_cxx::__normal_iterator<PatientRecord*,
std::vector<PatientRecord>>;_OI=
__gnu_cxx::__normal_iterator<PatientRecord*,
std::vector<PatientRecord>>]'
c:/mingw/i686-w64-mingw32/include/c++/bits/vector.tcc:145:2:
requiredfrom'std::vector<_Tp,_Alloc>::iterator
std::vector<_Tp,_Alloc>::_M_erase(std::vector<_Tp,
_Alloc>::iterator)[with_Tp=PatientRecord;_Alloc=
std::allocator<PatientRecord>;std::vector<_Tp,
_Alloc>::iterator=__gnu_cxx::__normal_iterator<PatientRecord*,
std::vector<PatientRecord>>;typenamestd::_Vector_base<_Tp,
_Alloc>::pointer=PatientRecord*]'
c:/mingw/i686-w64-mingw32/include/c++/bits/stl_vector.h:1147:58:
requiredfrom'std::vector<_Tp,_Alloc>::iterator
std::vector<_Tp,_Alloc>::erase(std::vector<_Tp,
_Alloc>::const_iterator)[with_Tp=PatientRecord;_Alloc=
TemplatesvsGenerics
222

std::allocator<PatientRecord>;std::vector<_Tp,
_Alloc>::iterator=__gnu_cxx::__normal_iterator<PatientRecord*,
std::vector<PatientRecord>>;typenamestd::_Vector_base<_Tp,
_Alloc>::pointer=PatientRecord*;std::vector<_Tp,
_Alloc>::const_iterator=__gnu_cxx::__normal_iterator<const
PatientRecord*,std::vector<PatientRecord>>;typename
__gnu_cxx::__alloc_traits<typenamestd::_Vector_base<_Tp,
_Alloc>::_Tp_alloc_type>::const_pointer=constPatientRecord*]'
..\vectest\main.cpp:22:34:requiredfromhere
..\vectest\main.cpp:8:19:error:'PatientRecord
PatientRecord::operator=(constPatientRecord&)'isprivate
PatientRecordoperator=(constPatientRecord&other){
return*this;}
Ifyouwadethroughthatnoisetothebottomwecanseetheerase()functionwantedtocall
theassignmentoperatoronPatientRecord,butcouldn'tbecauseitwasprivate.
Butwhydidvectorallowustodeclareavectorwithaclasswhichdidn'tmeetits
requirements?
Wewereabletodeclarethevector,usethestd::vector::push_back()functionbutwhenwe
calledstd::vector::erase()thecompilerdiscoveredsomedeeplynestederrorandthrew
theseerrorsbackatus.
ThereasonisthatC++onlygeneratescodefortemplateswhenitiscalled.Sothe
declarationwasnotinviolation,thepush_back()wasnotinviolationbuttheerasewas.
HowRusthelps
Rusthasaconceptsimilartotemplatescalledgenerics.Agenericsisastructortraitthat
takestypeparametersjustlikeatemplate.
Howeverbutthetypecanbeenforcedbysayingthetraitsthatitmustimplement.Inaddition
anyerrorsaremeaningful.
Saywewanttowriteagenericfunctionthatclonestheinputvalue:
fnclone_something<T>(value:T)->T{
value.clone()
}
TemplatesvsGenerics
223

Wehaven'tevencalledthefunctionyet,merelydefinedit.Whenwecompilethis,we'll
instantlygetanerrorinRust.
error:nomethodnamed clonefoundfortype Tinthecurrentscope
|
4|value.clone();
|^^^^^
|
=help:itemsfromtraitscanonlybeusedifthetraitis
implementedandinscope;thefollowingtraitdefinesanitem
`clone`,perhapsyouneedtoimplementit:
=help:candidate#1:`std::clone::Clone`
RustissayingweneversaidwhatTwasandbecausesome-random-typehasnomethod
calledclone()wegotanerror.Sowe'llmodifythefunctiontoaddatraitboundtoT.This
bindingsaysTmustimplementClone:
fnclone_something<T:Clone>(value:T)->T{
value.clone();
}
NowthecompilerknowsTmusthaveimplementCloneitisabletoresolveclone()andbe
happy.Nextweactuallycallittoseewhathappens:
structWhatHappensToMe;
letx=clone_something(10);
lety=clone_something(WhatHappensToMe{});
Wecanclonetheinteger10becauseintegersimplementtheClonetrait,butourempty
structWhatHappensToMedoesnotimplementClonetrait.Sowhenwecompileitwegetan
error.
TemplatesvsGenerics
224

error[E0277]:thetraitbound`main::WhatHappensToMe:
std::clone::Clone`isnotsatisfied
|
8|lety=clone_something(WhatHappensToMe{});
|^^^^^^^^^^^^^^^
|
=note:requiredby`main::clone_something`
Insummary,RustimprovesontemplatesbyTODO
Compilinggenericfunctions/structsevenwhentheyareunusedandoffermeaningfulerrors
immediately.
Allowustobindtraitstogenerictypestoconstrainwhatwecanpassintothem.
Offermeaningfulerrorsifweviolatetherequirementsofthetraitbounds
TemplatesvsGenerics
225

MultipleInheritance
C++allowscodetoinheritfrommultipleclassesandtheyinturncouldinheritfromother
classes.Thisgivesrisetothedreadeddiamondpattern.
e.g.DinheritsfromBandCbutBandCbothinheritfromA.SodoesDhavetwoinstances
ofAorone?
Thiscancausecompilererrorswhichareonlypartiallysolvedbyusingsomethingcalled
"virtualinheritance"toconvincethecompilertoshareAbetweenBandC.
i.eifweknewBandCcouldpotentiallybemultiplyinheritedwemightdeclarethemwitha
virtualkeywordintheirinheritance:
classB:publicvirtualA{
//...
};
classC:publicvirtualA{
};
classD:publicB,publicC{
//...
};
WhenDinheritsfromBandC,bothsharethesameinstanceofA.Butthatassumesthe
authorsofA,BandCwereawareofthisproblemarisingandcodedthemselveswiththe
assumptionthatAcouldbeshared.
Themoreusualnormalsolutionfordiamondpatternsis"don'tdoit".i.eusecompositionor
somethingtoavoidtheproblem.
HowRusthelps
Rustalsodoesnotuseclassinheritancesoproblemslikediamondpatternscannotexist.
HowevertraitsinRustcaninheritfromothertraits,sopotentiallyitcouldhavediamond-like
issues.Buttoensureitdoesn't,thebasetraitisimplementedseparatelyfromanytraitsthat
inheritfromit.
SoifstructDimplementstraitsB&CandtheyinheritfromA,thenA,BandCmusthave
implblocks.
MultipleInheritance
226

traitA{
//...
}
traitB:A{
//...
}
traitC:A{
//...
}
structD;
implAforD{
//...
}
implBforD{
//...
}
implCforD{
//...
}
MultipleInheritance
227

LinkerErrors
CandC++requiresyousupplyalistofallthe.objfilesthatformpartofyourlibraryor
executable.
Ifyouomitafilebyaccidentyouwillgetundefinedormissingreferences.Maintainingthis
listoffilesisanadditionalburdenofdevelopment,ensuringtoupdateyourmakefileor
solutioneverytimeyouaddafiletoyourproject.
HowRustHelps
Rustincludeseverythinginyourlibrary/executablethatisdirectlyorindirectlyreferenced
bymodcommands,startingfromyourtoplevellib.rsormain.rsandworkingalltheway
down.
Providingyoureferenceamodule,itwillbeautomaticallybuiltandlinkedintoyourbinary.
Ifyouusethe cargocommand,thentheabovealsoappliesforexternalcratesthatyoulink
with.Thecargocommandwillalsocheckforversionconflictsbetweenexternallibraries.If
youfindyourcargogeneratingerrorsaboutcompatibilityconflictsbetweencratesyoumay
beabletoresolvethembyupdatingtheCargo.lockfilelikeso:
cargoupdate
LinkerErrors
228

DebuggingRust
RustcompilesintomachinecodethesameasCandbenefitsfromsharingthesameABI
andcompilerbackendformatsasC/C++.
SoyoucandebugRustinthesamewayasC/C++.IfyoubuiltyourRustexecutableinagcc
compatiblebinaryformatyoucanjustinvokegdbonit:
gdbmy_executable
Rustcomeswithagdbwrapperscriptcalled rust-gdbthatloadsmacroswhichperform
syntaxhighlighting.
Enablingbacktrace
Ifyourcodeiscrashingbecauseofapanic!()youcangetabacktraceontheconsoleby
settingthe RUST_BACKTRACEenvironmentvariable.
#Windows
setRUST_BACKTRACE=1
#Unix/Linux
exportRUST_BACKTRACE=1
Findoutyourtargetbinaryformat
Ifyouareindoubtwhatyouaretargeting,youmayuse rustuptoshowyou.
c:\dev\visu>rustupshow
Defaulthost:x86_64-pc-windows-msvc
stable-x86_64-pc-windows-msvc(default)
rustc1.13.0(2c6933acc2016-11-07)
Orperhaps:
DebuggingRust
229

[foo@localhost~]$rustupshow
Defaulthost:x86_64-unknown-linux-gnu
stable-x86_64-unknown-linux-gnu(default)
rustc1.13.0(2c6933acc2016-11-07)
Theinformationwilltellyouwhichdebuggeryoucanusetodebugyourcode.
MicrosoftVisualStudio
IfyouhavetheMSVCtoolchain(32or64-bit)ortheLLVMbackendwillgeneratea.pdbfile
andbinarieswillbecompatiblewiththestandardMSVCruntime.
Todebugyourcode:
1. OpenVisualStudio
2. ChooseFile|Open|Project/Solution...
3. Selectthecompiledexecutable
4. Openasourcefiletodebugandsetabreakpoint
5. Clickthe"Start"button
GDB
GDBcanbeinvokeddirectlyfromthecommandlineorthroughaplugin/IDE.Fromthe
commandlineit'sa
TODO
LLDB
TODO
DebuggingRust
230

MemoryManagement
ThememorymodelofRustisquiteclosetoC++.StructuresthatyoudeclareinRustreside
onthestackortheyresideintheheap.
Stack
Thestackisamemoryreservedbytheoperatingsystemforeachthreadinyourprogram.
Stackisreservedforlocalvariablesbasedupontheirpredeterminedsizebymovingastack
pointerregisterforwardbythatamount.Whenthelocalvariablesgooutofscope,thestack
pointerreducesbythesameamount.
//Stackallocated
doublepi=3.141592735;
{
//Stackpointermovesasvaluesgoesinandoutofscope
intvalues[20]={0,1,2,3,...,19,20};
}
InC-stylelanguagesitisnormalforthestackineachthreadtobeasinglecontiguousslab
ofmemorythatrepresentsthe"worstcase"scenarioforyourprogrami.e.youwillnever
needanymorestackthanthethreadallocatedatstart.Ifyoudoexceedthestack,thenyou
causeastackoverflow.
Somelanguagessupporttheconceptofsplitorsegmentedstack.Inthiscase,thestackisa
seriesof"stacklets"joinedtogetherbyalinkedlist.Whenthestackisinsufficientforthenext
call,itallocatesanotherstacklet.
Thegcccansupportasegmentedstack,butitgreatlycomplicatesstackunwindingwhenan
exceptionisthrownandalsowhencallsaremadeacrosslinkerboundaries,e.g.betweena
segmented-stackawareprocessandanonsegmentedstackdynamiclibrary.
StackOverflows
Themainworryfromusingthestackisthepossibilityofastackoverflow,i.ethestack
pointermovesoutofthememoryreservedforthestackandstartstramplingonother
memory.
MemoryManagement
231

Thiscanoccurintwocommonwaysinisolationorcombination:
Deeplynestedfunctioncalls,e.g.arecursivefunctionthattraversesabinarytree,ora
recursivefunctionthatneverstops
Exhaustingstackbyusingexcessiveand/orlargelocalvariablesinfunctions,e.g.lotsof
64KBbytearrays.
C++
SomeC++compilerswon'tcatchanoverflowatall.Theyhavenoguardpageandthus
allowthestackpointertojustgrowwhereevermemorytakesituntiltheprogramis
destabilizedandcrashes.
Thegcccompilerhassupportsegmentedstacksbutasdescribedearliernotwithoutissue.
TheMSVCcompileraddsaguardpageandstackpointercheckswhenwhenthestack
pointercouldadvancemorethanapageinasinglejumpandpotentiallymisstheguard
page.
Rust
Rustusedtosupportasegmentedstackasameansofdetectingmemoryviolationbutsince
1.4hasreplaceditwithaguardpageattheendofthestackspace.Iftheguardpageis
touchedbyamemorywrite,itwillgenerateasegmentationfaultthathaltsthethread.Guard
pagesopenupasmallriskthatthestackcouldgrowwellinexcessoftheguardanditmight
takesometimeforawritetotheguardtogenerateafault.
Rustaspirestosupportstackprobecodegenerationonallplatformsatwhichpointitislikely
tousethatinadditiontoaguardpage.Astackprobeisadditionalgeneratedcodeon
functionsthatusemorethanapageofspaceforlocalvariablestotestiftheyexceedthe
stack.
Rustreducestheriskstackoverflowsinsomeindirectways.It'seasyinC++through
inheritanceorbycallingapolymorphicmethodinadvertentlysetoffarecursiveloop
Heap
Heapisamemorythatthelanguageruntimerequestsfromtheoperatingsystemandmakes
availabletoyourcodethroughmemoryallocationcalls
C++
MemoryManagement
232

char*v=(char*)malloc(128);
memset(v,0,128);
strcpy(v,"Helloworld");
//...
free(string);
double*values=newdouble[10];
for(inti=0;i<10;i++){
values[i]=double(i);
}
delete[]values;
Allocationsimplymeansaportionoftheheapismarkedasin-useandthecodeisprovided
withapointertothereservedareatodowhatitlikeswith.Freecausestheportiontobe
returnedtoitsfreestate,coalescingwithanyfreeareasthatitresidesnexttoinmemory.
Aheapcangrowandcodemightcreatemultipleheapsandmightevenbecompelledtoin
ordercontrolproblemssuchasheapfragmentation.
Rust
ToallocatememoryontheheapinRustyoudeclaredatainsideofaabox.Forexampleto
createa1kblockofbytes:
letx:Box<[u8]>=Box::new([0;1024]);
Manystructsinstd::andelsewherewillhaveastackbasedportionandalsouseuseheap
internallytoholdtheirbuffers.
Heapfragmentation
Heapfragmentationhappenswhencontiguousspaceintheheapislimitedbythepatternof
memoryallocationsthatitalreadycontains.Whenthishappensamemoryallocationcanfail
andtheheapmustbegrowntomakeitsucceed.Insystemswhichdonothavevirtual
memory/paging,memoryexhaustioncausedbyfragmentationcancausetheprogramor
eventheoperatingsystemtofailcompletely.
MemoryManagement
233

Theeasiestwaytoseefragmentationiswithasimpleexample.We'llpretendthereisno
housekeepingstructures,guardblocksorotherthingstogetintheway.Imaginea10byte
heap,whereeverybyteisinitiallyfree.
Nowallocate5bytesforobjectoftypeA.Theheapreserves5bytesandmarksthemused.
Nowallocate1byteforobjectoftypeB.Thisisalsomarkedused.
NowfreeobjectA.Thetheportionofheapismarkedunused.Nowwehaveablockof5
bytesfreeandablockwith4bytesfree.
Nowallocate2bytesforobjectoftypeC.Nowwehaveablockof3bytesfreeandablock
with4bytesfree.
Nowallocate5slotsforobjectoftypeA-Oopswecan't!Theheaphas7bytesfreebutthey
arenotcontiguous.Atthispointtheruntimewouldbeforcedtogrowtheheap,i.e.askthe
operatingsystemforanotherchunkofmemoryatwhichpointitcanallocate5bytesforA.
Theaboveassumestheheapisacontiguous,orthatmemorypagingmakesitseemso.On
somesystems,itmightbethattheheapisalinkedlistofchunks,inwhichcasetheallocated
spaceforAwouldhavetoresidebeinasinglechunk,thenewlyallocatedportionabove.
Thisisalsoanexageratedexample,butitdemonstrateshowheapcanhavespace,butnot
enoughtofufillyallocationswithoutgrowing.
Softwarerunninginembeddeddevicesareparticularlyvulnerabletofragmentationbecause
theydonothavevirtualmemory,havelowphysicalmemoryandnormallyhavetorunfor
days,weeksoryearsatatime.
OnemajorproblemforC++isthatheapfragmentationisalmostimpossibletoavoid.The
standardtemplatelibraryallocatesmemoryforvirtuallyallstringandcollectionwork,andifa
string/collectiongrowsthenitmayhavetoreallocatemorememory.
Theonlywaytomitigatetheissueistochoosethebestcollection,andtoreservecapacity
whereverpossible.
MemoryManagement
234

std::vector<double>values;
values.reserve(10);
for(inti=0;i<10;i++){
values.push_back(double(i));
}
Rustalsohasthisissueandstrings/collectionshavemethodstoreservecapacity.Butasa
consequenceofitsdesignitprefersthestackovertheheap.Unlessyouexplicitlyallocate
memorybyputtingitintoaBox,CellorRefCellyoudonotallocateitontheheap.
RAII
RAIIstandsforResourceAcquisitonIsInitalization.It'saprogrammingpatternthatties
accesstosomeresourcetheobject'slifetime
C++classesallowapatterncalledRAII().Aclassconstructoracquiressomeresource,the
destructorreleasesthatresource.Assoonastheclassgoesoutofscope,theresourceis
released.
TODOC++example
RustisinherentlyRAIIandenforcesitthroughlifetimes.Whenanobjectgoesoutofscope,
thethingitholdsisreleased.Rustalsoallowstheprogrammertoexplicitlydropastruct
earlierthanitsnaturallifetimeifthereisareasonto.
RAIIismostcommonlyseenforheapallocatedmemorybutitcanapplytofiles,system
handlesetc.
TODORustexample
MemoryManagement
235

Rust'sstandardlibrary
ThecorefunctionalityinRustisprovidedbyamodulecalled std.Thisisthestandard
runtimelibrary.
AswithitsC++namesake,everythingcanbereferencedthrougha std::namespace
prefixorviaa usestd::{foo}import.
Someofstdisimplicitlyavailablebyaspecial std::preludethatisautomaticallyused
(alongwithareferencetothestdcrate)withoutdeclaration.Thepreludecontains
functionalitythatvirtuallyallcodeislikelytouseandthereforeRustsparescodefromhaving
toimportit:
StringandToStringtrait
Iteratorstraitsofvariouskinds-Iterator,Exten,IntoIteratoretc.
Result<>andOption<>enums
ConversiontraitsAsRef,AsMut,Into,From
Vecheapallocatedvector
OthertraitssuchasDrop,Fn,FnMut,FnOnce,Box,Clone,Copy,Send,Sized,Sync,
PartialEq,PartialOrdetc.
Macrossuchasprintln!,format!,assert!etc.
//Youdon'tneedthese
externcratestd;
usestd::prelude::*;
Therearevarioussub-modulesunderstdthatconcernthemselveswithaspectsof
development.Herearejustsomeofthem:
1. clone–theClonetrait
2. cmp–Eq,Ord,PartialEq,PartialOrdtraits.Thesetraitsareusedforequalityand
orderingfunctionality.
3. collections-containsthestandardcollectiontypesforsequences,maps,sets,and
miscellaneous.e.g.VecandHashMaparemembersofthismodule.
4. env–environmentalhelpers-commandlinearguments,statuscodes,environment
variables,temporaryfolder
5. fmt–utilitiesforformattingandprintingstrings
6. fs-filesystemmanipulation
7. io–ReadandWritetraitsthatareimplementedbystreams/buffersinfilesystemand
networking,stdiofunctionality
Rust'sstd::library
236

8. mem–memoryprimitives
9. net–networking
10. path–pathmanipulation
11. process–spawn,fork,execetc.
C/C++libtoRustlibcrossreference
TODO
NotethatRust'sstdnamespacecontainsalotofstuffnotinthestandardCorC++libraries
andalotofthingsarenotdirectlyanalogous.ForexamplethestandardC/C++libraryhave
verylittletosayaboutsockets,orpathmanipulation,oratomicallyincrementingnumbers,or
creatingthreads.
C C++ Rust
T[S],e.g.charfoo[20] std::array(C++11) [T;S],e.g.letfoo:[u8;20]
=[0;20]
char*orchar[]withfunctions
suchasstrcmp,strcpy,strstr,
strdupetc.Pluswide
equivalentstothese.
std::string,
std::wstring,
std::u16string
(C++11),
std::u32string
(C++11)
&strorStringas
appropriate
- std::vector std::vec::Vecor
std::collections::VecDeque
- std::list std::collections::LinkedList
- std::set std::collections::HashSet,
std::collections::BTreeSet
- std::map std::collections::HashMap,
std::collections::BTreeMap
fopen,fclose,fread/fwrite,
fseeketc.
std::ofstream,
std::ifstream,
std::fstream
TODO
Mathfunctionssuchascos,sin,
tan,acos,asin,atan,pow,abs,
log,log10,floor,ceilaredefined
in
-
Mathfunctionsare
directionaccessiblefrom
f64.f32types.,e.g.
1.0f64.cos().
Notethatbecauseduetothedecimalpointbeingusedonafloat,youhavetoprefixf32or
f64toliteralswhenyoucallthemsothecompilercanfigureoutwhatyou'redoing.
Rust'sstd::library
237

StandardTraits
Sometraitsaresystemdefinedandinsomecasescanbederivedautomatically.
InotherstheycausethecompilertogenerateadditionalcodeforyousuchastheDroptrait
(describedinclassdestructorsection)
Drop
TheDroptraitallowsyoudosomethingwhenanobjectisdropped,suchasaddadditional
loggingorwhatever.
Copy
AstructimplementingaCopytraitcanbecopiedthroughassignment,i.e.ifyouassignato
bthenaandbnowhowcopiesoftheobject,independentofeachother.TheCopytrait
reallyonlyusefulwhenyouhavesmallamountsofdatathatrepresentatypeorvalueof
somekind.TODOcopyexample,e.g.structPlayingCard{suit:Suit,rank:Rank}Ifyoufind
yourselfwithatypethatislarger,orcontainsheapallocatedmemorythenyoushoulduse
clone.
Clone
AstructimplementingtheClonetraithasa.clone()method.UnlikeCopyyoumustexplicitly
.clone()theinstancetocreateanother.TODOcloneexample
Eq,PartialEq
TODOequality
Ord,PartialOrd
TODOordering
Rust'sstd::library
238

RustCookbook
Numbers
Convertanumbertoastring
Let'ssayyouhaveanintegeryouwanttoturnintoastring.
InC++youmightdooneofthefollowing:
constintvalue=17;
std::stringvalue_as_string;
//NonstandardCitoa()(alsonotthreadsafe)
value_as_string=itoa(value);
//OR_itoa()
charbuffer[16];
_itoa(value,buffer,10);
//OR
sprintf(buffer,"%d",value);
//OR
stringstreamss;
ss<<value;
value_as_string=ss.str();
//OR(boost)
value_as_string=boost::lexical_cast<std::string>(ivalue);
Allofthesehaveissues.Someareextensionstothestandard,othersmaynotbethread
safe,somemaybreakif valuewaschangedtoanothertype,e.g. longlong.
RustmakesitfareasierbecausenumericprimitivesimplementatraitcalledToString.The
ToStringtraithasato_string()function.Sotoconvertthenumbertostringisassimpleas
this:
RustCookbook
239

letvalue=17u32;
letvalue_as_string=value.to_string();
Thesameistrueforafloatingpointnumber:
letvalue=100.00345f32;
letvalue_as_string=value.to_string();
Convertanumbertoastringwithprecision/padding
InCyouwouldaddprecisionofpaddingusingprintfoperations:
doublevalue=1234.66667;
charresult[32];
sprintf(result,"%08.2d",value);
InC++youcouldusetheCway(andtobehonestit'seasierthanwhatiswrittenbelow),or
youcansetpaddingandprecisionthroughanostream:
//TODOvalidate
doublevalue=1234.66667;
ostringstreamss;
ss<<setfill('0')<<setw(8)<<setprecision(2)<<value;
InRustyoucanuseformat!()[https://doc.rust-lang.org/std/fmt/]forthispurposeanditis
similartoprintf/sprintf:
letvalue=1234.66667;
letvalue_as_string=format!("{:08.2}",value);
println!("value={}",value_as_string);
Output
value=01234.67
Convertanumbertoalocalizedstring
RustCookbook
240

Somelocaleswillusedotsorcommasforseparators.Somelanguageswillusedotsor
commasforthedecimalplace.Inordertoformatthesestringsweneedtomakeuseofthe
locale.
TODO
Convertastringtoanumber
InC/C++anumbermightbeconvertedfromastringtoanumberinanumberofways
intvalue=atoi(value_as_str);
TODO
InRustwehavea&strcontaininganumber:
letvalue_as_str="12345";
AnytypethatimplementsatraitcalledFromStrcantakeitstypefromastring.Allthe
standardprimitivetypesimplementFromStrsowecansimplysaythis:
letvalue_as_str="12345";
letvalue=i32::from_str(value_as_str).unwrap();
Notetheunwrap()attheend-theFromStr::from_str()returnsthevalueinsideaResult,to
allowforthepossibilitythatthestringcannotbeparsed.Productioncodeshouldtestfor
errorsbeforecallingunwrap()oritwillpanic.
Anotherwaytogetthestringistocallparse()onthe&strorStringitself.Inthiscase,you
useaslightlyoddlookingsyntaxnicknamed'turbofish'whichlookslikethis:
usestd::str::FromStr;
letvalue_as_str="12345";
letvalue=value_as_str.parse::<i32>().unwrap();
Thestring'simplementationofparse()isagenericthatworkswithanytypeimplementing
FromStr.Socalling parse::<i32>isequivalenttocalling i32::from_str().
RustCookbook
241

NoteoneimmediateadvantageofRustisitusesstringslices.Thatmeansyoucouldhavea
longstringwithmanynumbersseparatedbydelimitersandparsenumbersstraightoutof
themiddleofitwithoutconstructingintermediatecopies.
Convertingbetweennumerictypes
Convertingbetweennumerictypesisaseasyasusingthe"as"keyword.
letf=1234.42f32;
leti=fasi32;
println!("Value={}",i);
Theresultiniistheintegerpartoff.
Value=1234
Strings
Rustcomeswithsomeverypowerfulfunctionsthatareattachedtoevery&strandString
type.Thesemostlycorrespondtowhatyoumaybeusedtoonthestd::stringclassandin
booststringalgorithms.
Mostfind/match/trim/splitstringoperationsinRustareefficientbecausetheyneither
modifytheexistingstring,norreturnaduplicatetoyou.Insteadtheyreturnslices,i.e.a
pointerandalengthintoyourexistingstringtodenotetherangethatistheresult.
Itisonlyoperationsthatmodifythestringcontentsthemselvessuchascreatingupperor
lowercaseversionsthatwillreturnanewcopyofastring.
Trimmingastring
Spaces,tabsandotherUnicodecharactersdefinedaswhitespacecanbetrimmedfroma
string.
Allstringshaveaccesstothefollowingfunctions
fntrim(&self)->&str
fntrim_left(&self)->&str
fntrim_right(&self)->&str
RustCookbook
242

Notethesignaturesofthesefunctions-theyarenotmutable.Thefunctionsreturnasliceof
thestringthatexcludestheleadingand/ortrailingwhitespaceremoved.Inotherwordsitis
notduplicatingthestring,norisitmodifyingtheexistingstring.Insteaditisjusttellingyou
whatthetrimmedrangeiswithinthe&stryou'realreadylookingat.
So
letuntrimmed_str="thisistestwithwhitespace\t";
lettrimmed_str=untrimmed_str.trim();
println!("Trimmedstr=\"{}\"",trimmed_str);
Yields:
Trimmedstr="thisistestwithwhitespace"
Alsobeawarethattrim_left()andandtrim_right()aboveareaffectedbythedirectionalityof
thestring.
Moststringsreadfromleft-to-right,butstringsinArabicorHebrewarereadright-to-leftand
willstartwithacontrolcharacterthatsetstheirbasedirectionright-to-left.Ifthatcharacteris
present,trim_left()actuallytrimsfromtherightandtrim_right()trimsfromtheleft.
Getthelengthofastring
Every&strandStringhasalen()function.
letmessage="Allgoodthingscometothosewhowait";
println!("Length={}",message.len());
Notethatlen()isthelengthinbytes.Ifyouwantthenumberofcharactersyouneedtocall
message.chars().count(),e.g.
letmessage="文字列の長さ";
assert_eq!(message.chars().count(),6);
Splittingastring
StringslicesandStringhaveavarietyof splitmethodsthatreturnaniterablecollectionof
slicesonastring:
RustCookbook
243

letinput="20,30,400,100,21,-1";
letvalues:Vec<&str>=input.split(",").collect();
for(i,s)invalues.iter().enumerate(){
println!("Value{}={}",i,s);
}
Thestandard split()takesastringpatternforthedelimiterandreturnsa
std::str::Splitstructthatisandouble-endediteratorrepresentationofthematching
result.Wecouldcalltheiteratordirectlyifwesowishedbutthe collect()methodabove
putsthevaluesoftheiteratorintoa Vec<&str>.
Value0=20
Value1=30
Value2=400
Value3=100
Value4=21
Value5=-1
Astringcanalsobesplitonanindex,e.g.
let(left,right)="NoMisterBondIexpectyouto
die".split_at(14);
println!("Left={}",left);
println!("Right={}",right);
Notethatindexisthebyteindex!Thefunctionwillpaniciftheindexisinthecentreofa
UTF-8codepoint.
Anotherusefulfunctionis split_whitespacethatsplitsontabs,spaces,newlinesandother
Unicodewhitespace.Anyamountofwhitespaceistreatedasasingledelimiter.
//Splitwhitespace
forsin"Allgood\n\n\tthingstothosewho
wait".split_whitespace(){
println!("Part-{}",s);
}
Yieldstheoutput.
RustCookbook
244

Part-All
Part-good
Part-things
Part-to
Part-those
Part-who
Part-wait
Tokenizingastring
TODO
Joiningstringstogether
TODO
Gettingasubstring
TODO
Convertingastringbetweenupperandlowercase
Stringshavethesefunctionsforconvertingbetweenupperandlowercase:
fnto_lowercase(&self)->String
fnto_uppercase(&self)->String
ThesefunctionswillreturnanewStringthatcontainstheupperorlowercaseversionofthe
input.UpperandlowercasearedefinedbyUnicoderules.Languagesthathavenoupperor
lowercasestringsmayreturnthesamecharacters.
Doingacaseinsensitivecompare
TODO
Usingregularexpressionmatches
TODO
RustCookbook
245

DateandTime
Getthecurrentdateandtime
TODOtime_rs
UTC
TODOexplainwhatUTCisandwhymaintainingtimeinUTCisvitalEpochsetc.TODO
preambleaboutwhatanepochis,theUnixepochandotherepochs
Settingatimer
TODOsettingatimer
SystemtimevsUTC
TODOthereasontimersmightbesetinsystemuptimevstimersbeingsetinUTC.Answer
becauseusersandNTPcanchangetheUTCtimewhereasesystemtimeisrelativeto
bootup.Sosettingatimertorun10sfromnowwillalwaysworkagainstsystemtimewhere
settingatimertorun10sfromnowinUTCcouldfailiftheOSsetstimebackbyanhour.
Formattingadateasastring
TODOstandarddateformattingUTCTODOexample
Parsingadatefromastring
TODOparsingadatefromastring'sTODOexample
Performingdate/timearithmetic
Collections
Creatingastaticarray
Anarrayprimitiveconsistsofatypeandalength.e.g.a16kilobytearrayofbytescanbe
createdandzeroedlikethis:
RustCookbook
246

letvalues:[u8;16384]=[0;16384];
Thevariablespecifiesthetypeandlengthandtheassignmentoperatorassigns0toevery
element.
Thetype,lengthandvaluescanbeinitializedimplicitlyin-placelikethis:
letmy_array=["Cat","Dog","Fish","Donkey","Albatross"];
println!("{:?}",my_array);
Thisisanarrayof5&strvalues.Thecompilerwillcomplainifwetrytomixtypesinthe
array.Wecouldalsodeclarethearrayandmanipulateit:
letmutmy_array:[&'staticstr;5]=["";5];
//Setsomevalues
my_array[0]="Cat";
my_array[1]="Dog";
my_array[2]="Fish";
my_array[3]="Donkey";
my_array[4]="Albatross";
println!("{:?}",my_array);
Noteinthiscasewedeclaredthearray,eachelementreceivedanemptyvalue.Thenour
codeprogrammaticallysetthenewelementvalue.Thelatterformwouldobviouslybeuseful
forarraysthatchange.Thelatterwouldbeusefulforarrayswhichdonot.
Creatingadynamicvector
Avectorisalineararrayofvalues.Unlikeanarraywhichhasafixedlength,avectorcan
groworshrinkovertime.
Avectorcanbecreatedusingthevec!macrolikethis:
letmutmy_vector=vec![1984,1985,1988,1995,2001];
ThiscreatesamutableVecandprepopulatesitwith5values.Notehowthevec!macrocan
usesquarebracketsforitsarguments.Wecouldhaveusedroundbracketsanditwould
havemeantthesame.
AnewVeccanalsobemadeusingVec::new()orVec::with_capacity(size)
RustCookbook
247

letmutmy_array=Vec::new();
my_array.push("Hello");
letmy_presized_array=Vec::with_capacity(100);
ItisstronglyrecommendedyouuseVec::with_capacity()tocreateavectorwithenough
capacityformaximumnumberofelementsyouexpectthevectortocontain.Itpreventsthe
runtimefromhavingtoreallocateandcopydataifyoukeepexceedingtheexistingcapacity.
Italsosignificantlyreducesheapfragmentation.
Removingvaluesfromavector
Sometimesyouwanttostripoutvaluesfromalistwhichmatchsomepredicate.Inwhich
casethereisahandyfunctionforthatpurpose.TODO .retain
Sortingavector
Avectorcanbesortedbythenaturalsortorderoftheelementsitcontains:
letmutvalues=vec![99,-1,3,555,76];
values.sort();
println!("Values={:?}",values);
SortingisdoneusingtheOrdtraitandcallingOrd::cmp()ontheelementstocomparethem
toeachother.
ComparisoncanalsobedonethroughaclosureandVec::sort_by()
TODO .sort_byTODO .sort_by_key
Strippingoutduplicatesfromavector
Assumingyourvecissorted,youcanstripoutconsecutiveduplicateentriesusingdedup().
Thisfunctionwon'tworkandtheresultwillbeundefinedifyourvectorisnotsorted.TODO
.dedup
Creatingalinkedlist
Alinkedlistismoresuitablethanavectorwhenitemsarelikelytobeinsertedorremoved
fromeitherendorfrompointswithinthelist.
std::collections::LinkedList
RustCookbook
248

Creatingahashset
Ahashsetisauniquecollectionofobjects.Itisparticularlyusefulforremovingduplicates
thatmightoccurintheinput. std::collections::HashSet
Creatingahashmap
Ahashmapconsistsofakeyandavalue.Itisusedforlookupoperations
std::collections::HashMap
Iteratingcollections
TODO
Iteratoradaptors
TODO
Anadaptorturnstheiteratorintoanewvalue
.enum.map(X).take(N).filter(X)
Consumingiterators
Aconsumerisaconveniencewayofiteratingacollectionandproducingavalueorasetof
valuesfromtheresult.
.collect()
.find()willreturnthefirstmatchingelementthatmatchestheclosurepredicate.TODO
.fold()isawayofdoingcalculationsonthecollection.Ittakesabasevalue,andthen
callsaclosuretoaccumulatethevalueupontheresultofthelastvalue.TODOProcessing
collections
Localization
Unicodeconsiderations
TODO
Externalizingstrings
RustCookbook
249

TODO
Buildingstringsfromparameters
TODO
Creatingalocalizationfile
TODO
Logging
Filesandstreams
Rustcomeswithtwostandardmodules:
std::iocontainsvariousstreamrelatedtraitsandotherfunctionality.
std::fscontainsfilesystemrelatedfunctionalityincludingtheimplementationofIOtraits
toworkwithfiles.
Creatingadirectory
Adirectorycanbecreatedwith std::fs::DirBuilder,e.g.
letresult=
DirBuilder::new().recursive(true).create("/tmp/work_dir");
Filepaths
WindowsandUnixsystemshavedifferentnotationforpathseparatorsandanumberof
otherdifferences.e.g.Windowshasdriveletters,longpaths,andnetworkpathscalled
UNCs.
RustprovidesaPathBufstructformanipulatingpathsandaPathwhichactslikeasliceand
canbethefullpathorjustaportionofone.
TODOsimpleexampleofapathbeingcreated
TODOsimpleexampleofaPathsliceinactively
TODOsimpleexampleofrelativepathmadeabsolute
RustCookbook
250

Windowshasabunchofpathprefixessostd::path::Prefixprovidesawaytoaccessing
those.
TODOexampleofapathbeingmadefromadriveletterandpath
Openingafile
A Fileisareferencetoanopenfileonthefilesystem.Whenthestructgoesoutofscope
thefileisclosed.Therearestaticfunctionsforcreatingoropeningafile:
usestd::io::prelude::*;
usestd::fs::File;
letmutf=try!(File::open("myfile.txt"));
TODO
NotethatFile::open()opensafileread-onlybydefault.Toopenafileread-write,thereisan
OpenOptionsstructthathasmethodstosetthebehaviouroftheopenfile-read,write,
create,appendandtruncate.
e.g.toopenafilewithread/writeaccess,creatingitifitdoesnotalreadyexist.
usestd::fs::OpenOptions;
letfile=OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open("myfile.txt");
Writingtoafile
TODOsimpleexampleofopeningfiletowrite
Readinglinesfromafile
TODOsimpleexampleofopeningfiletextmode,printingcontents
Threading
RustCookbook
251

Rustactivelyenforcesthreadsafetyinyourcode.Ifyouattempttopassarounddatawhich
isnotmarkedthreadsafe(i.e.implementstheSynctrait),youwillgetacompileerror.Ifyou
usecodewhichisimplicitlynotthreadsafesuchasRc<>youwillgetacompileerror.
ThisenforcementmeansthatRustprotectsagainstdataraceconditions,howeverbeaware
itcannotprotectagainstotherformsofraceconditionsordeadlocks,e.g.thread1waitsfor
resourceB(heldbythread2)whilethread2waitsforresourceA(heldbythread1).
Creatingathread
Creatingathreadissimplewithaclosure.
TODO
Waitingforathreadtocomplete
TODO
Usingatomicreferencecounting
Rustprovidestworeferencecountingtypes.TypeRc<>isforcoderesidingonthesame
threadandsothereferencecountingisnotatomic.TypeArc<>isforcodethatrunson
differentthreadsandthereferencecountingisatomic.
AnArc<>canonlyholdaSyncderivedobject.WheneveryoucloneanArc<>oritslifetime
ends,thecounterisatomicallyincrementedordecremented.Thelastdecrementtozero
causestheobjecttobedeleted.
TODOexample
Lockingasharedresource
Messagepassingisapreferablewaytopreventthreadsfromsharingstatebutitsnot
alwayspossible.
ThereforeRustallowsyoutocreateamutexandlockaccesstoshareddata.Theguardthat
locks/unlocksthemutexprotectsthedataandwhentheguardgoesoutofscope,thedata
isreturned.
ThisstyleofguardiscalledTODO
Dataraceprotection
RustCookbook
252

Rustcanguaranteethatprotectionfromdataraces,i.e.morethanonethreadaccessing/
writingtothesamedataatthesametime.
HoweverevenRustcannotprotectagainstthemoregeneralproblemofraceconditions.e.g.
iftwothreadslockeachother'sdata,thenthecodewilldeadlock.Thisisaproblemthatno
languagecansolve.
Waitingformultiplethreadstofinish
TODO
Sendingdatatoathread
AnystructthatimplementstheSendtraitistreatedsafetosendtoanotherthread.Ofcourse
thatappliesto
Receivingdatafromathread
Athreadcanreceivemessagesandblockuntilitreceivesone.Thusitiseasytocreatea
workerthreadofsomekind.
TODO
Networking
Connectingtoaserver
TODO
Listeningtoasocket
TODO
InteractingwithC
Usinglibcfunctionsandtypes
CallingaClibrary
Generatingadynamiclibrary
RustCookbook
253

CallingWin32functions
Commondesignpatterns
Singleton
Asingletonhasoneinstanceeverinyourapplication.TODO
Factory
TODO
Observer
TODO
Facade
TODO
Flyweight
TODO
Adapter
Anadapteriswherewepresentadifferentinterfacetoaclientcallingtheadapterthanthe
interfacethecodeisimplementedin.Thismightbedonetomakesomelegacycode
conformtoanewinterface,ortomanage/hidecomplexitywhichmightleakoutintothe
client.
AsRustisarelativelynewlanguageyouaremostlikelytouseanadapterpatterntowrap
someexistingcodeinC.AcommonusefortheadapterinC++istowrapupaClibraryin
RAIIclassesorsimilar.
TODO
RustCookbook
254

