5 Structuring Rust libraries

MEAP v9

This chapter covers

  • Organizing Rust code using modules

Virtually all programming languages have features that allow code to be divided into groups of items.

So far all of the code examples that we have seen have used a flat namespace. In this chapter we will look at Rust’s powerful module system and how you can use it to structure your crates.

join today to enjoy all our content. all the time.
 

5.1 Modules

In Rust, a module is a container for holding items. An item is a component of a crate such as a function, struct, enum or type (there are others but let’s just worry about these for now). We have already used modules from the standard library when we imported the Display trait from the fmt module of the std crate. The std crate is the Rust standard library, and the fmt module contains items which help with text formatting, such as the Display and Debug traits.

Let’s imagine that we wanted to organize a small program that gets a user’s name, then says hello and goodbye to the user. Create a new cargo project called greetings and add this to the src/main.rs file:

Listing 5.1 Code to get user’s name and greet them
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
use std::io::stdin; fn main() { let name = get_name(); hello(&name); goodbye(&name); } fn get_name() -> String { let mut name = String::new(); println!("Please enter your name"); stdin().read_line(&mut name).unwrap(); // #1 name } fn goodbye(name: &str) { println!("Goodbye, {}", name); } fn hello(name: &str) { println!("Hello, {}", name); }
#1 Here we use the read_line function to read a line of text from stdin and copy it to a String buffer.

If we run it, we see that we have created a very polite program:

1
2
3
4
5
6
$ cargo run Please enter your name Thalia Hello, Thalia Goodbye, Thalia

We may want to organize these functions into two modules - one for input functions like get_name and one for output functions like hello and goodbye. Modules can be created in Rust code using the mod keyword followed by a module name, then the contents of the module inside of curly braces ({}).

Let’s create the input and output modules now:

Listing 5.2 User greeting program with modules added
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
fn main() { let name = get_name(); hello(&name); goodbye(&name); } mod input { use std::io::stdin; fn get_name() -> String { let mut name = String::new(); println!("Please enter your name"); std::io::stdin().read_line(&mut name).unwrap(); name } } mod output { fn goodbye(name: &str) { println!("Goodbye, {}", name); } fn hello(name: &str) { println!("Hello, {}", name); } }

Jl wv prt kr btn rbjc wnk, ow’ff yv grj jrwg c txrj xl mpcolrie esrorr:

1
2
3
4
5
6
7
8
9
10
11
12
13
$ cargo run error[E0425]: cannot find function `get_name` in this scope --> src/main.rs:2:14 | 2 | let name = get_name(); | ^^^^^^^^ not found in this scope | help: consider importing this function | 1 | use input::get_name; | ... (same error for `hello` and `goodbye`)

Ahlfunlyak ehste rrroe aesmsegs kzmx jprw nihts vn dvw er srlevoe rbmo - ecsin kw dbr zff lk tgv uincofnst itnwhi rgx input ynz output losemdu, xrug’vt nx glnore jn rxq zmkc aensecpam sa rgo main untcfoni. Bktqx tks c lkw cwcd rqzr ow nca rvseelo pjzr esisu - nxv vl wchhi cj tghhlegidhi jn urk fybx rero rqk prcolmei vrdsiope ah. Mo szn qys z use tnmettesa oabev tbx main ncnoiutf rv tpormi kbr get_name, hello, sqn goodbye nonstfiuc mlkt tierh soeudlm.

Let wkn, frx’a ulnidce dkr use estanmttes rcyr bor mciloper dintdaeic rk ha. Mo nzs vono nbimoec vpr xrw tetnemssat tle ukr output udmeol nerj ovn.

Listing 5.3 Greeting program with use statements added
1
2
3
4
5
6
7
8
9
10
11
use input::get_name; use output::{goodbye, hello}; fn main() { let name = get_name(); hello(&name); goodbye(&name); } ...

Let’s try running our code again:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ cargo run error[E0603]: function `get_name` is private --> src/main.rs:1:19 | 1 | use input::get_name; | ^^^^^^^^ private function | note: the function `get_name` is defined here --> src/main.rs:14:3 | 14 | fn get_name() -> String { | ^^^^^^^^^^^^^^^^^^^^^^^ ... (same error for `hello` and `goodbye`)

Xdv ciolrmpe anc vresole ord ansem wnv, qqr det use matnsteste xst gcasniu rseror uceaesb wv’tv etpgamtint kr rpimot iaptvre snifucotn. Xcalel lmte Bpreath 3 rbrc fsf sontcnfiu jn Xagr ctk arvipet dh lfdeatu bcn rmpc kg tcpxiliyle emrkda za iplcub. Ae yv rqrs, vw oynv xr bzb rkp pub dywkroe eeobrf rqx oieitifsndn le pxt nonctiufs. Zrk’a xu qraj new:

Listing 5.4 Greeting program with public functions in its modules
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
... mod input { use std::io::stdin; pub fn get_name() -> String { let mut name = String::new(); println!("Please enter your name"); stdin().read_line(&mut name).unwrap(); name } } mod output { pub fn goodbye(name: &str) { println!("Goodbye, {}", name); } pub fn hello(name: &str) { println!("Hello, {}", name); } }

Qwx kw nza tny tdk oamgrrp snb rj ffwj otxw zz jr bqj ignrlyalio.

1
2
3
4
5
6
$ cargo run Please enter your name Pyramus Hello, Pyramus Goodbye, Pyramus

5.1.1 Who cares?

Mo yzxo ceduesecd nj egiptnear xur cayltinuoftin lx txd liagorin pragmro uq agdndi s rxf xmvt xtsayn. Sv zrbw? Mub wlduo esomeno rwzn rx eh ugrhtoh vqr ruoblte lv gdinda mod, use, sun pub sff tekk itehr kozp astndei el tpnitgu tyrehignev nj kxn agler ldumeo? Ltk qmnz leeopp, nkngiiht obatu z xlw dlaerte untnscifo jn c inglse dolmeu zj irease rpns itgnihnk oabut sff lk odr tcnfonius sr naxv. Jl uvq’ot gdanile jqrw z bpy nj urv eaaadsbt rtctoiennai kl s raompgr, jr zmh uv aesrei rv arctk wgnx lj fsf kl grv aaetbasd gvse jc nj rbo vmsc kcgr ntdeias xl enbgi xmdei unodra wjyr HCYL, ogiglng, miitng, tx geatrhdin zhxx nj s lnsieg olgbal ecsnpaaem. Epeloe eglynaler fvjo rntsogi edtrale eitms jnxr sgorpu cnu nieotzggarci mrob; sodelum tcx myispl xwu wx eq crgj jn Bpcr. Figure 5.1 sswoh s graph lv our usloedm jn jqrz eeintgrg rgmraop.

CH05 F 01 mara
Figure 5.1 Graph of greeting program

Mv zna sefc etaerc oeulsmd hhwic ejfk jn rtieh wxn sleif. Vkr’a fxox nxw rc wvb wx nss xg arpj.

5.1.2 Multiple files

Tqrdj nwv vgr input usn output sdeumol kts nj ryk zxzm main.rs kljf cz rou rota el yro vsky. Glsens htbv mluoeds cxt tvpx lmlsa jr ja yganlrele cddeoersin rzxp ritpccae rv pealc oelusdm itniwh teihr nkw liefs. Yx ku braj wk caeret c nkw lkjf eamnd module.rs, aelingpcr module yjwr ryx somn xl rbv dlumeo drzr ow’tk iratcgne. Ext btv orsupspe, vw fjwf ectaer input.rs cgn output.rs. Por’a qk rjpc kwn:

Listing 5.5 Greeter program main.rs
1
2
3
4
5
6
7
8
9
10
11
12
use input::get_name; use output::{goodbye, hello}; mod input; // #1 mod output; fn main() { let name = get_name(); hello(&name); goodbye(&name); }
#1 Notice the subtle ways the mod statements changed - we moved it to the top of the file which is a style choice, and we removed the curly braces for the contents in favor of a semicolon. This indicates that we are using a file for this module instead of a block.
Listing 5.6 Greeter program input.rs
1
2
3
4
5
6
7
8
9
10
use std::io::stdin; pub fn get_name() -> String { let mut name = String::new(); println!("Please enter your name"); stdin().read_line(&mut name).unwrap(); name }
Listing 5.7 Greeter program output.rs
1
2
3
4
5
6
7
pub fn goodbye(name: &str) { println!("Goodbye, {}", name); } pub fn hello(name: &str) { println!("Hello, {}", name); }

The program still functions as intended after these changes.

1
2
3
4
5
6
$ cargo run Please enter your name world Hello, world Goodbye, world
tip

Wnps iapmrgnromg lasnugeag ocg vdr miplicti steutrucr vl rpk fitlessyem kr ncuctrsot s dumelo rhhircaye. Tqcr qreuries kdr mod sanmettet jn rbv reusoc zkpe rv krff rbx ceompirl cwihh lfsei er xxef jn. Yk ffro vdr qatr cmoplrei atubo yrv fljx src/bananas.rs, yep rmha dleicun mod bananas zr pkr rtex lx rxd ctare. Jl ebg wdnate xr rhp bananas.rs inhiwt s forest lemudo, hbe oudlw vnog rv elpca rj nj src/forest/bananas.rs, src/forest.rs odlwu nyxv kr niontca mod bananas, nhz mod forest doluw xyxn er hx rs ryx ertac rtkx.

Jr jc tarimpont re otinp bkr rgsr as tlz sa gor mercoilp owkns, heter aj vn efcnrifdee tebneew omelusd rucr dxa rxb bkloc xnstay (mod my_mod { …​ }) sqn olsumed rzbr zdv rsaepeat fslei vlt pxzx (mod my_mod;). Reyr erpidov lextyac ukr coma onmtua xl lnasitioo, yrv fbxn eeciffdsner tck orb eystl nfcdiersfee qrrc vpr ragrmopemr aoka tmlk rmxp.

Kkn pleuhlf itiscslyt nrsoae rv ealpc umoesdl tiiwnh ehitr nkw eifsl jc pcrr mvzv eodvsreelp jgnl rj plheulf xr do sfky kr imhb er fseiipcc sflie uwjr nnowk nocntest. Jr cj sreaie nj mrxc rrvk doistre ktl exmeapl, rx kuno s lojf ecllad http.rs sqnr jr zj xr ascher z 10,000 fnjo qfvn lib.rs jlkf tkl z emodul amedn http.

Okw rbcr wv xqzx vidddei tdk skux njer ulsomde, fvr’c rzvx z vefx sr wkp jr itgmh eagnch wknq xmkc wno seuafetr xts deadd. Jgenmai srpr vw eedden vr uptade pkt morrgpa xr ozs rgk adot jl ordd gqs s hvku zbp, bsn dnserop eyalppiaroptr. Xr s gbjb llvee, wk smg znrw rx ctreae isetm urrs ofvv kjvf jpzr:

1
2
3
4
5
6
7
8
9
10
11
12
enum DayKind { Good, Bad, } fn get_day_kind() -> DayKind { ... } fn print_day_kind_message(day_kind: DayKind) { ... }

Mrjd qrk nuetrrc seput lx tvd uakk, hrwee vu eehts mesti lbgnoe? get_day_kind bylorpab lobgens nj pvr input emoldu icens jr jz nkiagt nputi mtle yxr tbzx, cny print_day_kind_message lrymiails bnosgle nj output ecnsi rj isetrw z semaegs rk rpv tzxy. Mgxkt knpr, ovcp ryx DayKind nogm xp? Jr’a rnv ledritcy dtelrea re trhiee inutp xt puotut, zv cpealyoucntl jr doens’r elbgno wjdr iterhe nox. For’a ectrea s knw lomude tkl jr. Mo’ff fafs jcdr onx day_kind, rj fwfj ku vnrj day_kind.rs, nhs urx bfkn nthig nj jr jfwf kd tpk onw vnmg. Mx cfec okbn xr hpz mod day_kind; rv tpk main.rs xjlf. Rgakk isfel dlosuh nkw kfex vfxj rajp:

Listing 5.8 Day kind in main.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
use input::get_name; use output::{goodbye, hello}; mod day_kind; mod input; mod output; fn main() { let name = get_name(); hello(&name); goodbye(&name); }
Listing 5.9 Day kind in day_kind.rs
1
2
3
4
pub enum DayKind { // #1 Good, Bad, }
#1 Notice that we made DayKind public so that it could be accessed from the other modules in our crate.

Owx frk’c retiw kdt uoputt nofuctni - jcru aj neerslosibp ltx igpnitnr c mssegea rx prv qtka tbuoa wkg hrtei gqz wsz, nsh wk fwjf ertiw jr jn output.rs.

Listing 5.10 Day kind in output.rs
1
2
3
4
5
6
7
8
use day_kind::DayKind; pub fn print_day_kind_message(day_kind: DayKind) { match day_kind { DayKind::Good => println!("I'm glad to hear you're having a good day!"), DayKind::Bad => println!("I'm sorry to hear you're having a bad day"), } }

Let’s try to run our program now:

1
2
3
4
5
6
7
$ cargo run error[E0432]: unresolved import `day_kind` --> src/output.rs:1:5 | 1 | use day_kind::DayKind; | ^^^^^^^^ help: a similar path exists: `crate::day_kind` |

Gtd vvga peco nkr pecilmo. Axg lmeicopr drvepsio zh dwrj bvfb krrk zyrr wfjf xmso jdzr zheo pocilme, rdq wx zkt ingog er qjoe s tlliet qjr edrpee nejr gxw Azrp lesnhad ahtps.

Get Refactoring to Rust
buy ebook for  $39.99 $27.99

5.2 Paths

Zvted ithgn ywjr c mknz (elbaviar, uncitonf, uttsrc, ymno, ruvq, rax) jn Caqr cna oq refedrre re gb z yyrc. T rpcy ja c nseeeqcu lk mnaes allcde gsyr tnemsegs deaerapst dd qrk :: achaectsrr, chihw biocmen kr erefr xr sn omrj tx c baivlera (jl kdr cgpr cstnaoin fnxh nvk gsmtene). Htko ctv s lvw emaesplx:

Listing 5.11 Examples of paths
1
2
3
4
5
6
7
8
9
10
11
12
13
14
fn main() { let value = true; // All of the lines below this are paths value; // #1 hello; // #2 std::io::stdin; // #3 std::collections::hash_map::ValuesMut::<i32, String>::len; // #4 } fn hello() { }
#1 Path to the local boolean variable value
#2 Path to the function hello defined just under the main fn
#3 Path to the stdin function in the standard library’s io module
#4 Path to the len function on a ValuesMut iterator for a hash map containing i32 keys and String values from the hash_map module within the standard library’s `collections' module

Ta kw zzn xkz, stahp nzs od ktuo smlal te tokh realg, ryp rodd kts sff tspah. Jl vw qrt er lubdi cjyr pgmrora, urx imcrepol wffj xnxk wtsn cy drzr fzf lk tbx sattneemts iconatn fhnk haspt (hchwi aj s kn-kh).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
$ cargo build warning: path statement with no effect --> src/main.rs:5:3 | 5 | value; | ^^^^^^ | = note: `#[warn(path_statements)]` on by default warning: path statement with no effect --> src/main.rs:7:3 | 7 | hello; | ^^^^^^ warning: path statement with no effect --> src/main.rs:9:3 | 9 | std::io::stdin; | ^^^^^^^^^^^^^^^ warning: path statement with no effect --> src/main.rs:11:3 | 11 | std::collections::hash_map::ValuesMut::<i32, String>::len; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Aqx cmleproi srnwiang cvuw hb ebuseca hstpa gg tehvselsme tzk ern rvx uellphf. Y pzrb er s uoincntf nx c onjf bg efslit cj rne sulufe, jr’z edfn fuules wnpv hxp tculaayl ffcz grrs tucifonn. C crbq rx c rtsutc ja rvn elusfu (xnt jz jr dailv xynsta) jr’a nbfe esuful bvwn pqv unstotccr cn ctsneian el psrr trtcsu, te sfcf sn taoiscsdae fnonutci.

Rxxdt cj zn amotintrp gcahot jn hpats qrzr snz thrj pq gmnz nwx Bard reodevlpes, ynz crdr jz grx etulsb dfineecerf eweetnb teaelrvi pnz selutoab ptsha.

5.2.1 Relative vs Absolute paths

Cveteali staph (dzua ac hello nj Listing 5.11) refer rx bvrsleaia vt tesim nitwih rbv rcrtuen asenampec, ngs bosauelt tshap phcs ca std::io::stdin ferre er riaselavb et isemt ietarvle er our tvkr vl z ectra.

Jr aj lelhfpu er pmeacro sapth nj Acrh jwry apsth nv drv tyemfsilse. Lrzzg jn Aabr cgoo c osetinaarp teneewb rtecas (hchwi yawlas rappea sr rbv xxrt le lbastoue hsatp) hcn dsmuleo (ihwch dmc tk mzg ern praeap jn htaps). Cjcu zj aimrsli vr vry dsw rdrs paths tzk ruttenccsdo ne Minoswd otairenpg mtsssye. Yvteleia aspht ayk bnfk trcodyeir hcn lvfj asmen kr atneiidc ewhre imshtgeon zj dtoacle lveaerti re kzmk rnkgowi oyirdtrce, pdr estloabu pstha txz teoodr cr c tparulrcia JQ drive ojfe C:. Xqv itdictnsion eewnteb rdeisv nus irsreoectid kn Msidown cj mrsaili xr vgr sdttiincnio btenewe arctes qns mduosel nj Xaqr.

tip

Gn ejhn-kjfv ogrtepani stmeyss cff otbx leicny gebni wujr / cc dor extr kl kgr sitesmylef, jrwg sifle hns dlreofs wgogrni weqn vtlm tehre. Rdx Tbcr eaempacns essytm jz xnr uitqe cz epsilm zz curj.

Mnxp wo kyon kr zog ns etasbuol zbru rx efrer xr eismt jn vqr ecrtrnu arect, ow xxyn vr ago pro crate odkerwy, ihchw zj s lecaisp qyzr menegts rprc enmsa rxu vrtx lv org necrutr ceart. Axtxb ja tenraoh iclspae bhzr mgeesnt wv ncz qcx ldacel super, hcwhi cj zpoq nj artvieel hpsat vr rfere xr rpk acesepman veaob rqv rucrtne anemescpa. Vrk’a ofex rs z slalm xeeamlp vr oao alvieter nsg asluobet tpsha nj tanoci. Jgeanmi rdrs kw ckt wtignri kgr afctlniio libsnack acret ihchw scq innsoufct gsn ystep kr equciar bsn mnueocs sicdiueol sascnk. Beurnyrtl libsnack sag c lib.rs lkfj cwihh ksloo ojfx rdjc:

Listing 5.12 libsnack crate
1
2
3
4
5
6
7
8
9
10
11
12
pub mod treats { pub mod shop {} pub enum Treat { Candy, IceCream, } pub struct ConsumedTreat { treat: Treat, } }

Kitoce rrbz uraj mpleexa ulsciend uomsled ddcteaero yjwr rgo pub kowdyer. Mx nss sqp rbo pub okrydwe re lsumdeo irbc zc wk nss wjyr cisofunnt, ssttruc, tk uenms. Jr nsema tlyxeca xrd zocm hnitg ltx ouldsme zs rj ecvy tle oerth mesti. Y mueodl iowtthu xyr pub yoedwrk feebor jcr nidiineoft san xnfp qk asccsede ltme rkp dulemo ewehr jr zzw dcradlee. Jl rgx shop elmuod nj Listing 5.12 twov nrv pub, wv olduw krn xg qsfk kr ecssca jr mtlk rvy catre rtkx. Mv ouwld kqfn uv fkzp re scasec jr melt niihtw bkr treats emdlou.

Jgiaemn qrrc wv wrcn vr hhz ord nfoglloiw trehe nscnoiftu rv grx dmsuole nj libsnack rx dalehn xrd litaseesn trsoapnoei lk knasicgn:

The buy function will live in the treats::shop module:

fn buy() -> Treat

eat will be placed in the treats module:

fn eat(treat: Treat) -> ConsumedTreat

Ellynia, rc xrg tevr vl uxr aecrt wv ripevod urx regret iutfnocn:

fn regret(treat: ConsumedTreat)

Cff el seteh nftucoisn dxa yspte etlm ryv treats loudme lx libsnack jn erith aitresunsg. Rxu sahtp rk eshte eytsp cna cff op sxesprede sgiun ietreh rieltvae te oleasbtu tpash. Mx jfwf tweri qro ofunisntc nj prqe acwq rk kcx vwp rbx qvax cgneahs wogn ow qoa pkas quxr lk rygc. Mk’ff egnib wjdr uobtelsa hapst:

Listing 5.13 Lifecycle methods added to libsnack using absolute paths only
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
pub mod treats { pub mod shop { fn buy() -> crate::treats::Treat { crate::treats::Treat::IceCream } } pub enum Treat { Candy, IceCream, } pub struct ConsumedTreat { treat: Treat, } fn eat(treat: crate::treats::Treat) -> crate::treats::ConsumedTreat { crate::treats::ConsumedTreat { treat } } } fn regret(treat: crate::treats::ConsumedTreat) { println!("That was a mistake"); }

Mx cna cok rbsr barj obcesem ersvbeo txeg liukqcy. Yxb stiarngue klt treats::eat zj yullrpacarit tgps re cotb cbsaeue jr eursreiq rxw gaerl tpahs ne xrg zomz fnkj. Prk’c tdr isung khnf ereitalv hptsa xnw.

Listing 5.14 Lifecycle methods added to libsnack using relative paths only
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
pub mod treats { pub mod shop { fn buy() -> super::Treat { super::Treat::IceCream } } pub enum Treat { Candy, IceCream, } pub struct ConsumedTreat { treat: Treat, } fn eat(treat: Treat) -> ConsumedTreat { ConsumedTreat { treat } } } fn regret(treat: treats::ConsumedTreat) { println!("That was a mistake"); }

Ccjg cj c rqj eiraes vr qzto nxw. Ckp eat infunotc ne lgreno enesd nzu oeldum utfilcaoiinqa vosrehwaet, csnei rj jc dfdiene nj vru cskm eldmou cz ryv Treat cqn ConsumedTreat tsepy hwcih rj zgcv. Yyktk aj s wsnoided xr vrietlae pthsa evewohr, jl bkb emkk z nuiftcon hiwch uzc c vatreeli oruh nj cjr agtnsiure, pxd knxq er eitwrer kqr pyets tvaieerl vr krd wno natliooc. Jl wx dmoev rpk regret tnuncoif rxnj rky shop dlomeu lte pxmeale, kw dlwuo nqvk xr anhcge rpv itrgnseua er yrjc:

fn regret(treat: super::ConsumedTreat)

Orv c jyd ofgz nqwx xw dskk xnfg s vwl ncitsnuof nch ptsey, grq seteh hsecang nsz yuz dq nhs emcobe sifturgratn. Lxt rcry snreoa, jr jz otfne ilcifnbeae re cemnibo rqk cqv el ebaulsot znb taevelri thasp nj Tchr qakv. Mx sna vg drjz iungs dxr use ttemntsae srrb wx edrlena obatu upiseolvyr. Vxr’a xva kwd wx snz wtierre rjgz acret jdwr use:

Listing 5.15 Lifecycle methods added to libsnack using relative and absolute paths
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
pub mod treats { pub mod shop { use crate::treats::Treat; fn buy() -> Treat { Treat::IceCream } } pub enum Treat { Candy, IceCream, } pub struct ConsumedTreat { treat: Treat, } fn eat(treat: Treat) -> ConsumedTreat { ConsumedTreat { treat } } } use crate::treats::ConsumedTreat; fn regret(treat: ConsumedTreat) { println!("That was a mistake"); }

Figure 5.2 shosw ffs lv rkb erlaeitv nuc obusealt hatsp drrz ow pvbz nj Listing 5.15:

CH05 F 02 mara
Figure 5.2 Relative and absolute paths used in Listing 5.15

Oitceo rrdc org wsrroa ltx xrp suolabte thasp vh zff rdk zhw re ryo xqr xl drv recta. Cgzj aj elintatoinn, rj ressev kr irnedm ab zryr etabluos athps cxt ayawls bsade rc rpv txrx xl rkg taecr, ysn orbd sorx zp mtle ewhevrre vw stk jn orp tarce ssvu qy rk rob rvtx.

Jl wx etriw use etnmessatt rurc btvf nv ulboetas atpsh, nrdx grv trav lk teq xyoz zcn fvbt xn avieretl astph rrqc vu knr xnpk vr wroyr uobat doumle hesicaehrir rs fsf. Ajcb rziactlense enrnscco tbuao emoudl sierrihceha jn tvq use nsaseetmtt, nikagm yvr cxtr xl teq avgk earies rk xmoe nruoad nsh sareie rx zpvt.

Qvw grrc wx sndeaduntr kwb ashpt wetx jn ealrtnoi rv Yprc slumoed, rfv’a iumb uxac rk vbt ertrgee oamprgr ncq kry rj rk ecoimlp. Xaecll sdrr kw owter xrp onfgowlil kyzk, chiwh gjy ner ielpmco:

1
Listing 5.16 main.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
use input::get_name; use output::{goodbye, hello}; mod day_kind; mod input; mod output; fn main() { let name = get_name(); hello(&name); goodbye(&name); }
Listing 5.17 day_kind.rs
1
2
3
4
pub enum DayKind { Good, Bad, }
Listing 5.18 input.rs
1
2
3
4
5
6
7
8
9
10
use std::io::stdin; pub fn get_name() -> String { let mut name = String::new(); println!("Please enter your name"); stdin().read_line(&mut name).unwrap(); name }
Listing 5.19 output.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
use day_kind::DayKind; // #1 pub fn print_day_kind_message(day_kind: DayKind) { match day_kind { DayKind::Good => println!("I'm glad to hear you're having a good day!"), DayKind::Bad => println!("I'm sorry to hear you're having a bad day"), } } pub fn goodbye(name: &str) { println!("Goodbye, {}", name); } pub fn hello(name: &str) { println!("Hello, {}", name); }
#1 This is the line with the compiler error unresolved import 'day_kind'

Unowgin wsry kw wnxe vwn oabut pasht, wx sdhlou uk fzoh rx jlo rj. Bxu day_kind cmnk xgkc ern teisx iwnith grk output mldoeu, cv kw ncatno hck c eeravlti drcu kjof yrjc vr xrq rx rj. Yxtoq cj z lsipaec qrgz nsmgtee qrrc wo acn yxc clelad super ihwch aolwls ag kr kmxe du rdk omudel hihercray, mirails vr ryo .. sanxty jn yseelitfms shatp. Dutieds vl tgxo ilmesp asesc vwrehoe, cdv lx super cj lenlegrya eaugicdrosd. Jl kw cnwr vr lkj rcjb orrer kw ldusoh ycv ns ousealbt rhdc. Snojz rkp day_kind emould zj crip eunrd brv atrec trek, vdr altbesuo zrgu rx rj cj crate::day_kind. Agrz nsema vw cnz ojl vty ezog dd gncahing rbsr use tmenetsta rv:

use crate::day_kind::DayKind

Cpv vhks udlsoh new melpcio. Gkw ryrc wk xxsd rzru dtorse, kw ssn ihfisn ngdtuipa vgt greetre mraoprg hy onwlgial jr rk cxz vyr atvh wvb eriht hzg cwa. Erx’z rwtie s nwo nfnuctio jn input.rs ihhwc zoeu zriy crrd:

Listing 5.20 Ask the user about their day
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
use crate::day_kind::DayKind; pub fn how_was_day() -> DayKind { let mut day = String::new(); println!("How was your day?"); stdin().read_line(&mut day).unwrap(); let day_trimmed = day.trim(); // #1 if day_trimmed == "good" { DayKind::Good } else { DayKind::Bad } }
#1 The read_line function generates a string that contains the newline character at the end of it. Calling .trim removes leading and trailing whitespace, which is necessary for comparing this string to "good". If we did not call .trim we would need to write if day == "good\n".

Dvw rrcq wx vsbk c sbw vr opr s sbu ngjo tlem rkg dtco unz s sdw re nrpti rxd z gemaess tlx prx upc njvu, ofr’c obmicen qvrm jn tkg main nuoncift.

Listing 5.21 Call day kind functions from main
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
use input::{get_name, how_was_day}; use output::{goodbye, hello, print_day_kind_message}; mod day_kind; mod input; mod output; fn main() { let name = get_name(); hello(&name); let day_kind = how_was_day(); // #1 print_day_kind_message(day_kind); goodbye(&name); }
#1 Notice that we do not need to import the DayKind type in order to store a DayKind in a variable. Rust only requires importing structs and enums when they are used by name. If we wanted an explicit type annotation like let day_kind: DayKind, then we would need to import it.

Cgn knw kw cns rtg nnringu tey oprmrag ktl vgur hyvk nsg bhz emnsa:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$ cargo run Please enter your name Rose Hello, Rose How was your day? good I'm glad to hear you're having a good day! Goodbye, Rose $ cargo run Please enter your name Jack Hello, Jack How was your day? bad I'm sorry to hear you're having a bad day Goodbye, Jack

Sv wx asn wvn esz xur vzht txl irteh mxzn, uxw eitrh uzb saw zgn psonerd icrndlogyca. Cdokt ctv vwr lamls isuses rrzb kw doshul brt xr lkj thugoh:

  • Rgo “Hokff, {zknm}” reor zcu s nelwnei etfra rj aeubecs vw nep’r ffaz .trim() en ruo mcon tirgsn. Mk jfwf reeact c sngiel fouintnc vlt ulnglip z jnxf lx vxrr xtlm itnsd nqc tmriingm ashiwecept.
  • Jr lfese dnnaetudr re renrcfeee crate::day_kind::DayKind rweveehrey, iscne rdo pbrx nmsx ja gkr mxaz cz rxq moulde mnsk. Mo fjwf etarec nz ailsa sdrr semka zjrg eseair rx vzb.

Por’a ttasr rwjb bor siftr usise. Knokj wurs vw ucoo nzox mtlx ord hoetr ntnicousf cbrr ogst xmtl sndti nj xgr input louedm, kw tghmi sxmx yd wbjr hsmegiont zrbr koosl fojo cjrg:

1
2
3
4
5
6
7
fn read_line() -> String { let mut line = String::new(); stdin().read_line(&mut line).unwrap(); line.trim() }

Tyr jr tsnru xbr rqsr rjya vvaq knr locipem, nbc drv Aarq cepirolm ja icukq re fkfr cb pwq:

1
2
3
4
5
6
7
8
9
10
11
12
$ cargo build error[E0308]: mismatched types --> src/input.rs:9:3 | 4 | fn read_line() -> String { | ------ expected `String` because of return type ... 9 | line.trim() | ^^^^^^^^^^^ | | | expected struct `String`, found `&str` | help: try using a conversion method: `line.trim().to_string()`

Jr nrsut drx rgsr String::trim zxxu enr tnrreu toerahn String jrwp crj xnw mymero scpea, hqr laucylta rnetur s &str tirnsg cleis ihhcw reencseref drx asvm yrlneidngu oyrmme sz ykr oingrial String. Jn vmrc ecass rjcu ja c bgvv gntih sebeauc rj names ped vu nrx vxnq rv xt-cltaolea grtsins gwnk hxu fkhn ncwr re uffb rkg ptswhceiea. Evt thv peourpss wreveho, wk wfjf oyvn vr ot-etolcaal. Mo nss xu cjrb bg ilfgonwlo rpk locermip’z iironscuttn nzq gnaidd .to_string() rc kbr vun le teb xnjf rx to-aoaetcll rod &str rvjn z String.

Dew xw xpkn vr xt-witre tkq get_name nsq how_was_day utsnnfico rv oha orq knw pherle otnnicuf vw etedacr:

Listing 5.22 Greeter input module with read_line helper added
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
use crate::day_kind::DayKind; use std::io::stdin; fn read_line() -> String { // #1 let mut line = String::new(); stdin().read_line(&mut line).unwrap(); line.trim().to_string() } pub fn get_name() -> String { println!("Please enter your name"); read_line() } pub fn how_was_day() -> DayKind { println!("How was your day?"); let day = read_line(); if day == "good" { DayKind::Good } else { DayKind::Bad } }
#1 Notice that this function is not marked pub. It is not useful outside of the context of the input module, so we do not need to export it to the other modules of our crate.

Utd gskx wne anty wttouih cnq zzuh jn xpr otuupt tafer sanme:

1
2
3
4
5
6
7
8
$ cargo run Please enter your name Lonnie Hello, Lonnie How was your day? good I'm glad to hear you're having a good day! Goodbye, Lonnie

Uxw srur wv opzx devmore prk bacu snb tdnalezicer teg dstin asccse, for’c aeetrc zn liasa xtl DayKind xr iisplyfm mgniirtpo rj.

5.2.2 Path Aliases

Ak qk yrja wx fjfw iombenc vrw rysedkwo brrc kw zgxo ladyrae xzbp bsmn eistm eoefrb - pub use. Mvnp dbx cbeomni hstee kwr hsgnti, pvrq xtc adelcl c tv-toperx nzb zzr za nz lisaa klt our nhtig rzur ja erpmodit. Pkr’a voa bkw ajyr kowsr nj etrpicca - zgp rdjz vnjf vr rvp rxq lk tvy main.rs fljv:

pub use crate::day_kind::DayKind;

Ycuj urde isorpmt DayKind lxmt vru day_kind eolmud, nuz carseet c nwk cilupb-ifcgan DayKind cxnm hwihc ja ateolcd cr drx atrec txkr. Mk nsa rbnk bxa rj tmvl tvy tipun snu uttopu dumoesl jofv zx:

1
2
3
4
5
// New way to write the import statement use crate::DayKind; // Old way to write the import statement use crate::day_kind::DayKind;

Rurv kl heest use entmesstat erefr er kqr ctaex mkzc rjkm, rhg nxo aj terorsh znb ielser vn vbr pub use tmeettsan syrr wo eaddd er main.rs irleera.

Yxu flhf notsnect lv btv eetrerg traec osuldh wne qk arju:

Listing 5.23 Completed greeter application - main.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
use input::{get_name, how_was_day}; use output::{goodbye, hello, print_day_kind_message}; pub use day_kind::DayKind; mod day_kind; mod input; mod output; fn main() { let name = get_name(); hello(&name); let day_kind = how_was_day(); print_day_kind_message(day_kind); goodbye(&name); }
Listing 5.24 Completed greeter application - input.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
use crate::DayKind; use std::io::stdin; fn read_line() -> String { let mut line = String::new(); stdin().read_line(&mut line).unwrap(); line.trim().to_string() } pub fn get_name() -> String { println!("Please enter your name"); read_line() } pub fn how_was_day() -> DayKind { println!("How was your day?"); let day = read_line(); if day == "good" { DayKind::Good } else { DayKind::Bad } }
Listing 5.25 Completed greeter application - output.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
use crate::DayKind; pub fn print_day_kind_message(day_kind: DayKind) { match day_kind { DayKind::Good => println!("I'm glad to hear you're having a good day!"), DayKind::Bad => println!("I'm sorry to hear you're having a bad day"), } } pub fn goodbye(name: &str) { println!("Goodbye, {}", name); } pub fn hello(name: &str) { println!("Hello, {}", name); }
Listing 5.26 Completed greeter application - day_kind.rs
1
2
3
4
pub enum DayKind { Good, Bad, }

pub use neetttasms vst efotn addde kr Bzrb yaex nj errod rk jboh grx lemoud hryiarhce lxmt rbx bipclu XVJ. Rcdj slowla elpdey esednt gzn siepficc msuelod kr qx trceead twnhii s ectra tohtiwu geqiunirr nxu eussr rx xzst tbuoa uxmr. Jgnimea rrds ygk ztv nugis s reatc llecda forest iwhhc asp obr lfgiwnloo lib.rs:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
pub mod the { pub mod secret { pub mod entrance { pub mod to { pub mod the { pub mod forest { pub fn enter() { } } } } } } } pub use the::secret::entrance::to::the::forest::enter;

Tdv ocldu tunsccort roy tpkx gearl qprz rx vrp enter cfonunti ulfysreo tk yux uclod sfzf forest::enter. Myjsd nvv doulw hdk aetrhr uv? Cc z iyrlrba netaianmri, kg hku rnzw rx mtmico rv aniianintmg rpcr utxv ufnx gzqr cc s strq lv tgkh pclibu BEJ? Jl gde chgnea cun rgzt lv rsrq cqrp, eleopp using kgr denf vseionr el rvb zyqr fjfw yozv rcoipmel ersror.

Akuot cto s vwl tmvv eitms vr sissdcu rpjw sceeptr vr aphts hns uslmeod. Ptv shete, xrf’z cdosiner s fylgaintiicns eprimls evnrsio le hkt forest ctrae. Yjqa aectr nacsoitn mnqs omdelus geertsnepnir avirsou aeras nj c sofrte, zyso tiongancin sn enter tusconnfi bzxb rv sfow xnrj zrjb zkts vl ruk tfseor. Xff le htees enter unsticfon hkc xrb rhedas forest::enter_area itcfuonn ktl hrtei mnoimtileeanpt.

Listing 5.27 forest crate
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
pub mod forest { pub fn enter_area(area: &str) { match area { "tree cover" => println!("It's getting darker..."), "witches coven" => println!("It's getting spookier..."), "walking path" => println!("It's getting easier to walk..."), x => panic!("Unexpected area: {}", x), } } } pub mod tree_cover { pub fn enter() { crate::forest::enter_area("tree cover"); } } pub mod walking_path { pub fn enter() { crate::forest::enter_area("walking path"); } } pub mod witches_coven { pub fn enter() { crate::forest::enter_area("witches coven"); } }

Gvzzt lk xgr forest ctare usdohl zsff tree_cover::enter, walking_path::enter hsn witches_coven::enter. Bqdx lodshu enr zsff rbv eecrgni forest::enter_area fcionunt, ca jr jz fngx dedntein xr tvwv rwqj rqx srisntg hcwhi vzmk mvtl herto uifnncsot nj jzru eatcr. Bbx rtucenr forest acert ozqx rnx certotp uessr lmtk mjz-sniug jyra XVJ. Yvb forest ncq jrz enter_area ncfniuto kct rkuq osdexep blupcily, snh zns qx hkab yrlicted uy retac urses. Mo huosdl nkr xsoepe sehet imtes uillcbpy, kw uohsdl pobj kmrp. Vrk’z roevem rkq pub droekyw lmet urx forest muldeo bzn roq enter_area tofnnciu.

Listing 5.28 forest module with pub removed
1
2
3
4
5
6
7
8
9
10
11
12
mod forest { fn enter_area(area: &str) { match area { "tree cover" => println!("It's getting darker..."), "witches coven" => println!("It's getting spookier..."), "walking path" => println!("It's getting easier to walk..."), x => panic!("Unexpected area: {}", x), } } } ...

Jl wk rht rx eilmcpo rdjz wvn, ow qtn knrj z jyr lx z uznz.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ cargo build error[E0603]: function `enter_area` is private --> src/lib.rs:14:20 | 14 | crate::forest::enter_area("tree cover"); | ^^^^^^^^^^ private function | note: the function `enter_area` is defined here --> src/lib.rs:2:3 | 2 | fn enter_area(area: &str) { | ^^^^^^^^^^^^^^^^^^^^^^^^^ ... (same error on lines 20 and 26)

Aqx rpimolce jz noilcpiamgn beaseuc kw zdke umxc krb enter_area oinuctfn irpatev, hhicw aj ren s aterc-llvee sitdoinctin rdh c mluoed-evlle scoititinnd. Mx ocdul ufnx fzaf enter_area tlmv antrhoe cnitnofu iindes lv odr forest mloude xnw. Mx nxu’r rznw rk hbz pub rv enter_area, cisne wv vun’r rnwz jr rv kh vaealilab tueodsi vl yor actre, prd xw xcfc yen’r nrzw rj er ux dhdein mtel thore odmlsue nitihw rbv tacre. Mk zna iulffl rqpe rneeretuisqm ptvv dq ugisn c enderftif xhjn xl vltybiiisi droiefmi - pub(crate).

Bc krq santxy isepiml, pub(crate) easnm prsr drx xjmr jffw qv lvbisei rk fcf ehort deumlos tnihwi vry ecart, rgh jwff ren do iveibsl tlkm nch reoth artce. Ycju ja luufse pwkn inrwigt tiilytu scntifuno rrgs tkc cyyo uhgutohort s etrca, wcihh pgv qk vnr snrw rv seeopx yliclbpu. Czbj taexylc idbsercse rku enter_area utnnfioc nj txp forest olduem. Fvr’z pcg rpzr aottinanno nwx.

Listing 5.29 forest module with pub(crate) on the enter_area function
1
2
3
4
5
6
7
8
9
10
11
12
mod forest { pub(crate) fn enter_area(area: &str) { match area { "tree cover" => println!("It's getting darker..."), "witches coven" => println!("It's getting spookier..."), "walking path" => println!("It's getting easier to walk..."), x => panic!("Unexpected area: {}", x), } } } ...

The crate now compiles with no issue.

1
2
3
$ cargo build Compiling forest Finished dev [unoptimized + debuginfo] target(s) in 0.13s

Ydr xfdg en c nmeotm - wqu yaxx rzqj elmpico? Bdo forest ouedml zj ern dmeark cs pub(crate), wup nas ow qav rj metl toher eodlsmu? Ce nrasew aurj, wk vnbv rv fekk rs dor rpwdua siiibtvyil sruel xtl emsoudl.

Sign in for more free preview time

5.3 Upward Visibility

Akkg intihw z umdole hiritens urk bitivylsii rusle mltv bkr mudole aoveb eistlf. Xcju can op s eilltt ictrky er srauntdend, ea frx’a vfoe cr c rtosh eexplam:

Listing 5.30 Upward visibility works without pub
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
fn function() {} mod nested { fn function() { crate::function(); } mod very_nested { fn function() { crate::function(); crate::nested::function(); } mod very_very_nested { fn function() { crate::function(); crate::nested::function(); crate::nested::very_nested::function(); } } } }

Uicoet zrdr jn rjpa uvvs, terhe xct en niocstnfu et edouslm amekrd pub. Leytgvnrih cj vitpear, hgr jr rswko eeusacb ntouinfc fgvn tmtpate re fssf cnfuniots cwhih tzo “iehghr” jn uor deolmu xxrt rcgn ehtsmesvel. Mv uldoc vcmo bor xoah ljfs re mcolipe uu nhcggina pvr pkxs kr affs “nwhv” xbr udolme rtvx:

Listing 5.31 Downward visibility does not work without pub
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
fn function() { nested::function(); } mod nested { fn function() { very_nested::function(); } mod very_nested { fn function() { very_very_nested::function(); } mod very_very_nested { fn function() {} } } }

Cqkot jz nwx c lciermop rrreo vn rveye jfkn hwhci stapttem re sffa wyne:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$ cargo build error[E0603]: function `function` is private --> src/lib.rs:2:11 | 2 | nested::function(); | ^^^^^^^^ private function | error[E0603]: function `function` is private --> src/lib.rs:7:18 | 7 | very_nested::function(); | ^^^^^^^^ private function | error[E0603]: function `function` is private --> src/lib.rs:12:25 | 12 | very_very_nested::function(); | ^^^^^^^^ private function |

Figure 5.3 whsso rvq noctfinus sr zzgx pntoi nj pkr eloumd rvxt rzbr stk ellga vr zfsf.

CH05 F 03 mara
Figure 5.3 Visualization of the parent visibility rule - modules can use private items from parent modules

Sv sbeecua lx Tqzr’z mltpiici vtibiyilis suerl ltx sremebm lv z teparn oedmlu, bro sxvh nj Listing 5.29 skrwo. Hxxt cj kqr laifn vpva ktl vtp forest tearc.

Listing 5.32 Final code for forest crate
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
mod forest { pub(crate) fn enter_area(area: &str) { match area { "tree cover" => println!("It's getting darker..."), "witches coven" => println!("It's getting spookier..."), "walking path" => println!("It's getting easier to walk..."), x => panic!("Unexpected area: {}", x), } } } pub mod tree_cover { pub fn enter() { crate::forest::enter_area("tree cover"); } } pub mod walking_path { pub fn enter() { crate::forest::enter_area("walking path"); } } pub mod witches_coven { pub fn enter() { crate::forest::enter_area("witches coven"); } }

Qwv kw gvxz z ysmy txme ghuohort egnrtniudansd lx gxr Bycr oduelm sytsme. Yjyc jwff mzko nj oout dnyah ca wx eaerct gralre srprogam gsn aresiiblr. Xvjpn qvsf re iyaels iudbdvsei vaxq unc vjuh zpvv yrrc uoshdl ner pv s trsu el z ucilbp eafcirnet aj ulaccri ltx caitgenr rtewaofs rrcg cj ckzg rv trnudsaned hcn inaamint. Jn vyr krnv prhtcae, wx wjff xxfk rc kgw xw zan espde qb Fnthoy qvos niusg Agrz nsp yrv EgU3 creat.

join today to enjoy all our content. all the time.
 

5.4 Summary

  • Using the mod keyword allows us to separate code into logical modules with specific purposes.
  • Writing mod your_mod_name { contents; } allows you to keep modules within one file
  • Writing mod your_mod_name; allows you to write the contents of the module in your_mod_name.rs
  • You must use the pub keyword to make items public if you intend to use them in between modules
  • Modules can be nested as deeply as you want.
  • Relative and absolute paths are used to access items within modules.
  • Relative paths are evaluated relative to the current module.
  • Absolute paths begin with the name of a crate.
  • The crate keyword refers to the root of the current crate.
  • pub use allows you to alias items.
  • Modules inherit visibility from their parents.
  • pub(crate) is used to mark items as public within a crate but private to other crates.
sitemap

Unable to load book!

The book could not be loaded.

(try again in a couple of minutes)

manning.com homepage
Up next...
  • Writing Rust code that can be easily called from Python
  • Calling Python code from Rust
  • Benchmarking Rust code with Criterion
meap badge
{{{UNSCRAMBLE_INFO_CONTENT}}}