tabla de contenido · el juego de las adivinanzas la cena de los filósofos rust dentro de otros...

Post on 26-Oct-2020

9 Views

Category:

Documents

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

0

1

1.1

1.2

1.3

2

2.1

2.2

2.3

3

3.1

3.2

3.3

3.4

3.5

3.6

3.7

3.8

3.9

3.10

4

4.1

4.2

4.3

4.4

4.5

4.6

4.7

4.8

4.9

TabladecontenidoIntroducción

PrimerosPasos

InstalandoRust

¡Hola,mundo!

¡Hola,Cargo!

AprendeRust

ElJuegodelasAdivinanzas

LaCenadelosFilósofos

RustdentrodeotrosLenguajes

RustEfectivo

LaPilayelMontículo

Pruebas

CompilaciónCondicional

Documentación

Iteradores

Concurrencia

ManejodeErrores

FFI

BorrowyAsRef

CanalesdeDistribución

SintaxisySemantica

EnlacesaVariables

Funciones

TiposPrimitivos

Comentarios

if

Ciclos

Pertenencia

ReferenciasyPréstamo

TiemposdeVida

ElLenguajedeProgramacionRust

2

4.10

4.11

4.12

4.13

4.14

4.15

4.16

4.17

4.18

4.19

4.20

4.21

4.22

4.23

4.24

4.25

4.26

4.27

4.28

4.29

4.30

4.31

4.32

4.33

4.34

4.35

4.36

5

5.1

5.2

5.3

5.4

5.5

5.6

Mutabilidad

Estructuras

Enumeraciones

Match

Patrones

SintaxisdeMétodos

Vectores

CadenasdeCaracteres

Genéricos

Traits

Drop

iflet

ObjetosTrait

Closures

SintaxisUniversaldeLlamadaaFunciones

CratesyMódulos

`const`y`static`

Atributos

Alias`type`

ConversiónentreTipos

TiposAsociados

TipossinTamaño

OperadoresySobrecarga

CoercionesDeref

Macros

ApuntadoresPlanos

`unsafe`

RustNocturno

PluginsdelCompilador

EnsambladorenLinea

Nostdlib

Intrínsecos

ItemsdeLenguaje

EnlaceAvanzado

ElLenguajedeProgramacionRust

3

5.7

5.8

5.9

5.10

6

7

PruebasdeRendimiento

SintaxisBoxyPatones

PatronesSlice

ConstantesAsociadas

Glosario

InvestigaciónAcadémica

ElLenguajedeProgramacionRust

4

%ElLenguajedeProgramaciónRust

¡Bienvenido!EstelibroteenseñaráacercadelLenguajedeProgramaciónRust.Rustesunlenguajedeprogramacióndesistemasenfocadoentresobjetivos:seguridad,velocidadyconcurrencia.Rustlograestosobjetivossintenerunrecolectordebasura,haciendoloutilparaunnumerodecasosdeusoparaloscualesotroslenguajesnosontanbuenos:embeberenotroslenguajes,programasconrequerimientosespecíficosdetiempoyespacio,escrituradecódigodebajonivel,comocontroladoresdedispositivoysistemasoperativos.Rustmejoraporsobreloslenguajesactualesenestenichoatravésdeunnumerodechequeosentiempodecompilaciónquenoincurrenningunapenalidadentiempodeejecución,eliminandoalmismotiempolascondicionesdecarrera.Rusttambiénimplementa'abstraccionesconcerocosto',abstraccionesquesesientencomolasdeunlenguajedealtonivel.Aunasí,Rustpermitecontrolprecisotalycomounlenguajedebajonivelloharia.

“ElLenguajedeProgramaciónRust”estadivididoensietesecciones.Estaintroduccióneslaprimera.Despuésdeesta:

PrimerosPasos-ConfiguratumaquinaparaeldesarrolloenRust.AprendeRust-AprendeprogramaciónenRustatravésdepequeñosproyectos.RustEfectivo-ConceptosdealtonivelparaescribirexcelentecódigoRust.SintaxisySemántica-CadapiezadeRust,divididaenpequenospedazos.RustNocturno-Característicastodavíanodisponiblesenbuildsestables.Glosario-Referenciadelostérminosusadosenellibro.InvestigaciónAcadémica-LiteraturaqueinfluencioRust.

Despuésdeleerestaintroducción,dependiendodetupreferencia,querrásleer‘AprendeRust’o‘SintaxisySemántica’:‘AprendeRust’siquierescomenzarconunproyectoo‘SintaxisySemántica’,siprefierescomenzarporlomáspequeñoaprendiendounúnicoconceptodetalladamenteantesdemovertealsiguiente.Abundantesenlacescruzadosconectandichaspartes.

Contribuyendo

LosarchivosfuentedeloscualesestelibroesgeneradopuedenserencontradosenGithub:github.com/goyox86/elpr-sources

UnabreveintroducciónaRust¿EsRustunlenguajeenelcualestaríasinteresado?Examinemosunospequeñosejemplosdecódigoquedemuestranalgunasdesusfortalezas.

ElLenguajedeProgramacionRust

5Introducción

ElconceptoprincipalquehaceúnicoaRustesllamado‘pertenencia’(‘ownership’).Consideraestepequeñoejemplo:

fnmain(){

letmutx=vec!["Hola","mundo"];

}

Esteprogramacreaununavariablellamadax.ElvalordeestavariableesVec<T>,un‘vector’,quecreamosatravésdeunamacrodefinidaenlabibliotecaestandar.Estamacrosellamavec,lasmacrossoninvocadasconun!.TodoestosiguiendounprincipiogeneralenRust:hacerlascosasexplícitas.Lasmacrospuedenhacercosassignificativamentemáscomplejasquellamadasafunciones,esporelloquesonvisualmentedistintas.El!ayudatambiénalanálisissintáctico,haciendolaescrituradeherramientasmásfácil,locualestambiénimportante.

Hemosusadomutparahacerxmutable:EnRustlasvariablessoninmutablespordefecto.Mástardeenesteejemploestaremosmutandoestevector.

Esimportatemencionarquenonecesitamosunaanotacióndetiposaquí:sibienRustesestaticamentetipado,nonecesitamosanotareltipodeformaexplicita.Rustposeeinferenciadetiposparabalancearelpoderdeeltipadoestáticoconlaverbosidaddelasanotacionesdetipos.

Rustprefiereasignacióndememoriadesdelapilaquedesdeelmontículo:xespuestodirectamenteenlapila.Sinembargo,eltipoVec<T>asignaespacioparaloselementosdelvectorenelmontículo.Sinoestasfamiliarizadoconestadistinciónpuedesignorarlaporahoraoecharunvistazo‘LaPilayelMonticulo’.Rustcomounlenguajedeprogramacióndesistemas,tedalahabilidaddecontrolarcomolamemoriaesasignada,perocomoestamoscomenzandonoestanrelevante.

Anteriormentemencionamosquela‘pertenencia’esnuevoconceptoclaveenRust.EnterminologíaRust,xesel‘dueño’delvector.Estosignificaquecuandoxsalgadeámbito,lamemoriaasignadaaelvectorseraliberada.EstoeshechoporelcompiladordeRustdemaneradeterministica,sinlanecesidaddeunmecanismocomounrecolectordebasura.Enotraspalabras,enRust,nohacesllamadasafuncionescomomallocyfreeexplícitamente:elcompiladordeterminademaneraestáticacuandosenecesitaasignaroliberarmemoria,einsertaesasllamadasporti.Erraresdehumanos,peroloscompiladoresnuncaolvidan.

Agreguemosotralíneaanuestroejemplo:

ElLenguajedeProgramacionRust

6Introducción

fnmain(){

letmutx=vec!["Hola","mundo"];

lety=&x[0];

}

Hemosintroducidootravariable,y.Enestecaso,yesuna‘referencia’aelprimerelementodeelvector.LasreferenciasenRustsonsimilaresalosapuntadoresenotroslenguajes,peroconchequeosdeseguridadadicionalesentiempodecompilación.Lasreferenciasinteractuanconelsistemadepertenenciaatravésdeel‘prestamo’(‘borrowing’),ellastomanprestadoaloqueapuntan,envezdeadueñarsedeello.Ladiferenciaesquecuandolareferenciasalgadeámbito,lamemoriasubyacentenoseraliberada.Desereseelcasoestaríamosliberandolamismamemoriadosveces,locualesmalo.

Agreguemosunaterceralínea.Dichalínealuceinocenteperocausaunerrordecompilación:

fnmain(){

letmutx=vec!["Hola","mundo"];

lety=&x[0];

x.push("foo");

}

pushesunmetodoenlosvectoresqueagregaunelementoalfinaldelvector.Cuandotratamosdecompilarelprogramaobtenemosunerror:

error:cannotborrow`x`asmutablebecauseitisalsoborrowedasimmutable

x.push("foo");

^

note:previousborrowof`x`occurshere;theimmutableborrowprevents

subsequentmovesormutableborrowsof`x`untiltheborrowends

lety=&x[0];

^

note:previousborrowendshere

fnmain(){

}

^

¡Uff!ElcompiladordeRustalgunasvecespuedeproporcionarerroresbiendetalladosyestavezunadeellas.Comoelerrorloexplica,mientrashacemoslavariablemutablenopodemosllamarapush.Estoesporqueyatenemosunareferenciaaunelementodelvector,y.Mutaralgomientrasexisteunareferenciaaelloespeligroso,porquepodemos

ElLenguajedeProgramacionRust

7Introducción

invalidarlareferencia.Enestecasoenespecifico,cuandocreamoselvector,solohemosasignadoespacioparadoselementos.Agregaruntercerosignificaríaasignarunnuevosegmentodememoriaparatodosloselementos,copiartodoslosvaloresanterioresyactualizarelapuntadorinternoaesamemoria.Todoesoestabien.Elproblemaesqueynoseriaactualizado,generandoun‘punterocolgante’.Locualestamal.Cualquierusodeyseriaunerrorenestecaso,yelcompiladornoshaprevenidodeello.

Entonces,¿cómoresolvemosesteproblema?Haydosenfoquesquepodríamostomar.Elprimeroeshacerunacopiaenlugardeunareferencia:

fnmain(){

letmutx=vec!["Hola","mundo"];

lety=x[0].clone();

x.push("foo");

}

Rusttienepordefectosemánticademovimiento,entoncessiqueremoshacerunacopiadealgunadata,llamamoselmétodoclone().Enesteejemployyanoesunareferenciaaelvectoralmacenadoenx,sinounacopiadesuprimerelemento,"Hola".Debidoaquenotenemosunareferencianuestropush()funcionaperfectamente.

Sirealmentequeremosunareferencia,necesitamosotraopción:asegurarnosdequenuestrareferenciasalgadeámbitoantesquetratamosdehacerlamutación.Deestamanera:

fnmain(){

letmutx=vec!["Hola","mundo"];

{

lety=&x[0];

}

x.push("foo");

}

Conelparadicionaldellaveshemoscreadounámbitointerno.ysaldrádeámbitoantesquellamemosapush(),entoncesnohayproblema.

Esteconceptodepertenencianoessolobuenoparaprevenirpunteroscolgantes,sinounconjuntoenterodeproblemas,comoinvalidacióndeiteradores,concurrenciaymás.

ElLenguajedeProgramacionRust

8Introducción

%PrimerosPasos

EstaprimeraseccióndellibrotepondráandandoenRustysusherramientas.Primero,instalaremosRust.Después,elclásicoprograma‘HolaMundo’.Finalmente,hablaremosdeCargo,elsistemadeconstrucciónymanejadordepaquetesdeRust.

ElLenguajedeProgramacionRust

9PrimerosPasos

%InstalandoRust

¡ElprimerpasoparausarRustesinstalarlo!ExistenunnúmerodemanerasparainstalarRust,perolamásfácilesusarelscriptrustup.SiestásenLinuxounaMac,todoloquenecesitashaceres(sinescribirlos$s,soloindicaneliniciodecadacomando):

$curl-sf-Lhttps://static.rust-lang.org/rustup.sh|sh

Siestáspreocupadoacercadelainseguridadpotencialdeusarcurl|sh,porfavorcontinúaleyendoyvenuestradeclaración.Siéntetelibredeusarlaversiondedospasosdelainstalaciónyexaminarnuestroscript:

$curl-f-Lhttps://static.rust-lang.org/rustup.sh-O

$shrustup.sh

SiestásenWindows,porfavordescargaelinstaladorde32bitsoelinstaladorde64bitsyejecutalo.

DesintalandoSidecidesqueyanoquieresmásRust,estaremosunpocotristesperoestábien,ningúnlenguajedeprogramaciónesparatodoelmundo.Simplementeejecutaelscriptdedesinstalación:

$sudo/usr/local/lib/rustlib/uninstall.sh

SiusasteelinstaladordeWindowssimplementevuelveaejecutarel.msiyestetedarálaopcióndedesinstalar.

Algunaspersonas,conmuchoderecho,sevenperturbadascuandolesdicesquedebencurl|sh.Básicamente,alhacerlo,estásconfiandoenquelabuenagentequemantieneRustnovaahackeartucomputadoryhacercosasmalas.¡Esoesbueninstinto!SieresunadeesaspersonasporfavorechaunvistazoaladocumentaciónacercadecompilarRustdesdeelcodigofuente,olapáginaoficialdedescargasdebinarios.

¡Ah!,tambiéndebemosmencionarlasplataformassoportadasoficialmente:

Windows(7,8,Server2008R2)Linux(2.6.18omayor,variasdistribuciones),x86andx86-64OSX10.7(Lion)omayor,x86yx86-64

ElLenguajedeProgramacionRust

10InstalandoRust

NosotrosprobamosRustextensivamenteendichasplataformas,yalgunasotraspocas,comoAndroid.Estassonlasquegarantizanfuncionardebidoaquetienenlamayorcantidaddepruebas.

Finalmente,uncomentarioacercadeWindows.RustconsideraWindowscomounaplataformadeprimeraclaseencuantoarelease,perosisomoshonestos,laexperienciaenWindowsnoestanintegradacomoladeLinux/OSX.¡Estamostrabajandoenello!Sialgonofunciona,esunbug.Porfavorhaznoslosaber.TodosycadaunodeloscommitssonprobadosenWindowsjustocomoencualquierotraplataforma.

SiyatienesRustinstalado,puedesabrirunaconsolayescribir:

$rustc--version

Deberíasverelnúmerodeversión,hashdelcommit,fechadelcommitylafechadecompilación:

rustc1.0.0(a59de37e92015-05-13)(built2015-05-14)

Silohasvisto,Rusthasidoinstaladosatisfactoriamente.¡Felicitaciones!

Esteinstaladortambiéninstalaunacopiadeladocumentaciónlocalmenteparaquepuedasleerlaaúnestandodesconectado.EnsistemasUNIX,/usr/local/share/doc/rusteslalocación.EnWindows,eseneldirectorioshare/doc,dentrodedondeseaquehayasinstaladoRust.

Sino,hayunnúmerodelugaresenlosquepuedesobtenerayuda.ElmásfácileselcanaldeIRC#rustenirc.mozilla.org,alcualpuedesaccederconMibbit.SigueeseenlaceyestaráshablandoconRusteros(atontoapodoqueusamosentrenosotros),yteayudaremos.Otrosexcelentesrecursossonelforodeusuarios,yStackOverflow.

ElLenguajedeProgramacionRust

11InstalandoRust

%¡Hola,mundo!

AhoraquehasinstaladoRust,escribamostuprimerprograma.Estradiciónquetuprimerprogramaencualquierlenguajeseaunoqueimprimaeltexto“¡Hola,mundo!”alapantalla.Lobuenodecomenzarconunprogramatansimpleesqueverificasnosoloqueelcompiladorestainstalado,sinoqueestafuncionando.Imprimirinformaciónalapantallaesunacosamuycomún.

Laprimeracosaquedebemoshacerescrearunarchivoendondeponernuestrocódigo.Amimegustacrearundirectorioproyectosenmidirectoriodeusuarioymantenertodosmisproyectosallí.ARustnoleimportadónderesideelcódigo.

Estollevaencuestiónaotroasuntoquedebemosaclarar:estaguíaasumiráqueposeesunafamiliaridadbásicaconlalineadecomandos.Rustnodemandanadaconrespectoatusherramientasdeediciónodóndevivetucódigo.SiprefieresunIDEalainterfazdelineadecomandos,probablementedeberíasecharunvistazoaSolidOak,odondeseaquelospluginsesténparatuIDEfavorito.Existenunnúmerodeextensionesdecalidadvariadaendesarrolloporlacomunidad.ElequipodetrásdeRusttambiénpublicapluginsparavarioseditores.ConfigurartueditoroIDEescapadelosobjetivosdeéstetutorial,chequealadocumentaciónparatuconfiguraciónespecífica.

Dichoesto,creemosundirectorioennuestrodirectoriodeproyectos.

$mkdir~/proyectos

$cd~/proyectos

$mkdirhola_mundo

$cdhola_mundo

SiestásenWindowsynoestásusandoPowerShell,el~podríanofuncionar.Consultaladocumentacionespecíficaparatuterminalparamayordetalle.

Creemosunnuevoarchivodecódigofuente.Llamaremosmain.rsanuestroarchivo.LosarchivosRustterminanconlaextensión.rs.Siestásusandomásdeunapalabraentunombredearchivo,usaunsubguion:hola_mundo.rsenvezdeholamundo.rs.

Ahoraquetienestuarchivoabiertoescribeestoenel:

fnmain(){

println!("¡Hola,mundo!");

}

Guardaloscambiosenelarchivo,yescribelosiguienteenlaventanadetuterminal:

ElLenguajedeProgramacionRust

12¡Hola,mundo!

$rustcmain.rs

$./main#omain.exeenWindows

¡Hola,mundo!

¡Éxito!Ahoraveamosquehapasadoendetalle.

fnmain(){

}

EstaslineasdefinenunafunciónenRust.Lafunciónmainesespecial:eselprincipiodetodoprogramaRust.Laprimeralíneadice:"Estoydeclarandounafunciónllamadamainlacualnorecibeargumentosynoretornanada."Siexistieranargumentos,estosiríandentrodeparéntesis((and)),ydebidoaquenoestamosretornandonadadeestafunción,podemosomitireltipoderetornocompletamente.Llegaremosaestomásadelante.

Tambiénnotarasquelafunciónestáenvueltaenllaves({and}).Rustrequieredichasllavesdelimitandotodosloscuerposdefunción.Estambiénconsideradobuenestilocolocarlallavedeaperturaenlamismalineadeladeclaracióndelafunción,conunespaciointermedio.

Losiguienteesestalinea:

println!("¡Hola,mundo!");

Estalíneahacetodoeltrabajoennuestropequeñoprograma.Hayunnúmerodedetallesquesonimportantesaquí.Elprimeroesqueestásangradoconcuatroespacios,notabulaciones.Porfavor,configuratueditorainsertarcuatroespaciosconlateclatab.Proveemosalgunasconfiguracionesdeejemploparavarioseditores.

Elsegundopuntoeslaparteprintln!().EstoesllamaraunamacroRustmacro,queescomosehacemetaprogramaciónenRust.Siestofueseencambiounafunción,luciriaasi:println().Paranuestrosefectos,nonecesitamospreocuparnosacercadeestadiferencia.Solosaberquealgunasvecesverás!,yqueestosignificaqueestasllamandoaunamacroenvezdeunafunciónnormal.Rustimplementaprintln!comounamacroenvezdecomounafunciónporbuenasrazones,peroesoesuntópicoavanzado.Unaúltimacosapormencionar:LasmacrosenRustsondiferentesdelasmacrosenC,silashasusado.Noestésasustadodeusarmacros.Llegaremosalosdetalleseventualmente,porahorasimplementedebesconfiarennosotros.

ElLenguajedeProgramacionRust

13¡Hola,mundo!

Acontinuación,"¡Hola,mundo!"esunacadenadecaracteres.Lascadenasdecaracteressonuntópicosorprendentementecomplejoenlenguajesdeprogramacióndesistemas,yéstaconcretamenteesunacadenadecarateres‘asigandaestáticamente’.Sitegustaríaleeracercadeasignacióndememoria,echaunvistazoalapilayelmontículo,peroporahoranonecesitashacerlosinolodeseas.Pasamosestacadenadecaracterescomounargumentoaprintln!quienasuvezimprimelacadenadecaracteresalapantalla.¡Fácil!

Finalmente,lalineaterminaconunpuntoycoma(;).Rustesunlenguajeorientadoaexpresiones,loquesignificaquelamayoríadelascosassonexpresiones,envezdesentencias.El;seusaparaindicarquelaexpresiónhaterminado,yquelasiguienteestálistaparacomenzar.LamayoríadelaslíneasdecódigoenRustterminanconun;.

Finalmente,compilaryejecutarnuestroprograma.Podemoscompilarconnuestrocompiladorrustcpasándoleelnombredenuestroarchivodecódigofuente:

$rustcmain.rs

Estoessimilaragccorclang,siprovienesdeCoC++.Rustemitiráunbinarioejecutable.Puedesverloconls:

$ls

mainmain.rs

OenWindows:

$dir

main.exemain.rs

Haydosarchivos:nuestrocódigofuente,conlaextensión.rs,yelejecutable(main.exeenWindows,mainenlosdemás)

$./main#omain.exeenWindows

Loanteriorimprimenuestrotexto¡Hola,mundo!anuestraterminal.

SiprovienesdeunlenguajedinámicocomoRuby,PythonoJavascript,probablementenoestésacostumbradoaverestosdospasoscomoseparados.Rustesunlenguajecompilado,locualsignificaquepuedescompilartuprograma,dárseloaalguienmás,yéstenonecesitatenerRustinstalado.Silesdasaalguienunarchivo.rbo.pyo.js,estenecesitatenerunaimplementacióndeRuby/Python/JavaScript,perosólonecesitasuncomandoparaambos,compilaryejecutartuprograma.Todoesacercadebalanceentreventajas/desventajaseneldiseñodelenguajes,yRusthaelegido.

ElLenguajedeProgramacionRust

14¡Hola,mundo!

Felicitaciones,hasescritooficialmenteunprogramaRust.¡EsoteconvierteenunprogramadorRust!Bienvenido.

Acontinuaciónmegustaríapresentarteotraherramienta,Cargo,elcualesusadoparaescribirprogramasRustparaelmundoreal.Solousarrustcestabienparacosassimples,peroamedidaquetuproyectocrece,necesitarásalgoqueteayudeaadministrartodaslasopcionesqueestetiene,asícomohacerfácilcompartirelcodigoconotraspersonasyproyectos.

ElLenguajedeProgramacionRust

15¡Hola,mundo!

%Hola,Cargo!

CargoesunaherramientaquelosRusterosusancomoayudaparaadministrarsusproyectosRust.Cargoestaactualmenteenestadopre-1.0,ydebidoaelloestodavíauntrabajoenproceso.SinembargoyaeslosuficientementebuenoparamuchosproyectosRust,yseasumequelosproyectosRustusaranCargodesdeelprincipio.

Cargoadministratrescosas:lacompilacióndetucódigo,descargadelasdependenciasquetucódigonecesita,ylacompilacióndedichasdependencias.Enprimerainstanciatucódigonotendráningunadependencia,esporelloqueestaremosusandosololaprimerapartedelafuncionalidaddeCargo.Eventualmente,agregaremosmas.DebidoaquecomenzamosusandoCargodesdeelprincipioserafácilagregardespués.

SihasinstaladoRustatravésdelosinstaladoresoficialesentoncesdeberíastenerCargo.SihasinstaladoRustdealgunaotramanera,podríasecharunvistazoaelREADMEdeCargoparainstruccionesespecificasacercadecomoinstalarlo.

MigrandoaCargoConvirtamosHolaMundoaCargo.

ParaCarguificarnuestroproyecto,necesitamosdoscosas:CrearunarchivodeconfiguraciónCargo.toml,ycolocarnuestroarchivodecódigofuenteenellugarcorrecto.Hagamosesaparteprimero:

$mkdirsrc

$mvmain.rssrc/main.rs

Notaquedebidoaqueestamoscreandounejecutable,usamosmain.rs.Siquisiéramoscrearunabiblioteca,deberíamosusarlib.rs.Locacionespersonalizadasparaelpuntodeentradapuedenserespecificadasconunaclave[[[lib]]or[[bin]]]crates-customenelarchivoTOMLdescritoacontinuación.

Cargoesperaquetusarchivosdecódigofuenteresidaneneldirectoriosrc.Estodejaelnivelraízparaotrascosas,comoREADMEs,informacióndelicencias,ytodoaquellonorelacionadocontucódigo.Cargonosayudaamantenernuestrosproyectosagradablesyordenados.Unlugarparatodo,ytodoensulugar.

Acontinuación,nuestroarchivodeconfiguración:

$editorCargo.toml

ElLenguajedeProgramacionRust

16¡Hola,Cargo!

Aseguratetetenerestenombrecorrecto:necesitaslaCmayuscula!

Colocaestodentro:

[package]

name="hola_mundo"

version="0.0.1"

authors=["Tunombre<tu@ejemplo.com>"]

EstearchivoestaenformatoTOML.Dejemosqueseaelmismoquienseexplique:

ElobjetivodeTOMLesserunformatodeconfiguraciónminimofacildeleerdebidoaunasemánticaobvia.TOMLestadiseñadoparamapeardeformain-ambiguaaunatablahash.TOMLdeberíaserfácildeconvertirenestructurasdedatosenunaampliavariedaddelenguajes.

TOMLesmuysimilaraINI,peroconalgunasbondadesextra.

Unavezquetengasestearchivoessulugar,deberíamosestarlistosparacompilar!Pruebaesto:

$cargobuild

Compilinghola_mundov0.0.1(file:///home/tunombre/proyectos/hola_mundo)

$./target/debug/hola_mundo

Hola,mundo!

Bam!Construimosnuestroproyectoconcargobuild,yloejecutamoscon./target/debug/hola_mundo.Podemoshacerlosdospasosenunoconcargorun:

$cargorun

Running`target/debug/hola_mundo`

Hola,mundo!

Nótesequenoreconstruimoselproyectoestavez.Cargodeterminoquenohabíamoscambiadoelarchivodecódigofuente,asíquesimplementeejecutoelbinario.Sihubiéremosrealizadounamodificación,deberíamoshaberlovistohaciendolosdospasos:

$cargorun

Compilinghola_mundov0.0.1(file:///home/tunombre/proyectos/hola_mundo)

Running`target/debug/hola_mundo`

Hola,mundo!

ElLenguajedeProgramacionRust

17¡Hola,Cargo!

Estononoshaaportadomuchoporencimadenuestrosimpleusoderustc,peropiensaenelfuturo:cuandonuestrosproyectossehaganmascomplicados,necesitaremoshacermascosasparalograrquetodaslaspartescompilencorrectamente.ConCargo,amedidaquenuestroproyectocrece,simplementeejecutamoscargobuild,ytodofuncionaradeformacorrecta.

Cuandonuestroproyectoestafinalmentelistoparalaliberacion,puedesusarcargobuild--releaseparacompilarloconoptimizaciones.

TambiénhabrasnotadoqueCargohacreadounarchivonuevo:Cargo.lock.

[root]

name="hola_mundo"

version="0.0.1"

EstearchivoesusadoporCargoparallevarelcontroldelasdependenciasusadasentuaplicación.Porahora,notenemosninguna,yestaunpocodisperso.Nuncadeberíasnecesitartocarestearchivoportucuenta,solodejaaCargomanejarlo.

Esoestodo!Hemosconstruidosatisfactoriamentehola_mundoconCargo.Aunquenuestroprogramaseasimple,estausandogranpartedelasherramientasrealesqueusarasporelrestodetucarreraconRust.PuedesasumirqueparacomenzarvirtualmentecontodoproyectoRustharáslosiguiente:

$gitclonealgunaurl.com/foo

$cdfoo

$cargobuild

UnProyectoNuevoNotienesquepasarportodoeseprocesocompletocadavezquequierascomenzarunproyectonuevo!Cargoposeelahabilidaddecrearundirectorioplantillaenelcualpuedescomenzaradesarrollarinmediatamente.

ParacomenzarunproyectonuevoconCargo,usamoscargonew:

$cargonewhola_mundo--bin

Estamospasando--binporqueestamoscreandounprogramabinario:siestuviéramoscreandounabiblioteca,loomitiríamos.

EchemosunvistazoaloqueCargohageneradoparanosotros:

ElLenguajedeProgramacionRust

18¡Hola,Cargo!

$cdhola_mundo

$tree.

.

├──Cargo.toml

└──src

└──main.rs

1directory,2files

Sinotieneselcomandotreeinstalado,probablementepodríasobtenerlomedianteelmanejadordepaquetesdetudistribución.Noesnecesario,peroesciertamenteútil.

Estoestodoloquenecesitasparacomenzar.PrimeroveamosnuestroCargo.toml:

[package]

name="hola_mundo"

version="0.0.1"

authors=["TuNombre<tu@ejemplo.com>"]

Cargoarellenadoestearchivoconvalorespordefectobasadoenlosargumentosqueleproporcionasteytuconfiguraciónglobalgit.PodriasnotartambienqueCargohainicializadoeldirectoriohola_mundocomounrepositoriogit.

Aquiestaelcontenidodesrc/main.rs:

fnmain(){

println!("Hola,mundo!");

}

Cargohageneradoun"Hola,mundo!"paranosotros,yaestaslistoparaempezaracodear.Cargoposeesupropiaguialacualcubretodassuscaracterísticasconmuchamasprofundidad.

Ahoraquehemosaprendidolasherramientas,comencemosenrealidadaaprendermasdeRustcomolenguaje.EstoeslabásequeteservirábienporelrestodetutiempoconRust.

Tienesdosopciones:Sumergirteenunproyectocon‘AprendeRust’,ocomenzardesdeelfondoytrabajarhaciaarribacon‘SintaxisySemántica’.Programadoresdesistemasmasexperimentadosprobablementepreferiran‘AprendeRust’,mientrasqueaquellosprovenientesdelenguajesdinamicospodriantambiendisfrutarlo.Gentediferenteaprendediferente!Escojeloquefuncionemejorparati.

ElLenguajedeProgramacionRust

19¡Hola,Cargo!

%AprendeRust

¡Bienvenido!EstaseccióntieneunospocostutorialesqueteenseñaránRustatravésdelaconstruccióndeproyectos.Obtendrásunavisióngeneral,perotambiénleecharemosunvistazoalosdetalles.

Siprefieresunaexperienciamásalestilo‘deabajohaciaarriba’,echaunvistazoaSintaxisySemántica.

ElLenguajedeProgramacionRust

20AprendeRust

%JuegodeAdivinanzas

Paranuestroprimerproyecto,implementaremosunproblemaclásicodeprogramaciónparaprincipiantes:unjuegodeadivinanzas.Comofuncionaeljuego:Nuestroprogramageneraraunnumeroenteroaleatorioentreunoycien.Nospediraqueintroduzcamosunacorazonada.Despuesdehaberproporcionadonuestronumero,estenosdirásiestuvimosmuypordebajoymuyporencima.Unavezqueadivinemoselnumerocorrecto,nosfelicitara.Suenabien?

ConfiguraciónInicialCreemosunnuevoproyecto.Andaatudirectoriodeproyectos.RecuerdascomocreamosnuestraestructuradedirectoriosyunCargo.tomlparahola_mundo?Cargoposseuncomandoquehaceesopornosotros.Probemoslo:

$cd~/proyectos

$cargonewadivinanzas--bin

$cdadivinanzas

Pasamoselnombredenuestroproyectoacargonew,juntoconelflag--bin,debidoaqueestamoscreandounbinario,envezdeunabiblioteca.

EchaunvistazoalCargo.tomlgenerado:

[package]

name="adivinanzas"

version="0.1.0"

authors=["TuNombre<tu@ejemplo.com>"]

Cargoobtieneestainformacióndetuentorno.Sinoestacorrecta,corrigela.

Finalmente,Cargohageneradoun‘Hola,mundo!’paranosotros.Echaunvistazoasrc/main.rs:

fnmain(){

println!("Hello,world!");

}

TratemosdecompilarloqueCargonoshaproporcionado:

ElLenguajedeProgramacionRust

21ElJuegodelasAdivinanzas

$cargobuild

Compilingadivinanzasv0.1.0(file:///home/tu/proyectos/adivinanzas)

Excelente!Abretusrc/main.rsotravez.Estaremosescribiendotodonuestrocodigoenestearchivo.

Antesdecontinuar,dejamemostrarteuncomandomasdeCargo:run.cargorunesunaespeciedecargobuild,peroconladiferenciadequetambienejecutaelbinarioproducido.Probemoslo:

$cargorun

Compilingadivinanzasv0.1.0(file:///home/tu/proyectos/adivinanzas)

Running`target/debug/adivinanzas`

Hola,mundo!

Grandioso!Elcomandorunesmuyútilcuandonecesitasiterarrapidoenunproyecto.Nuestrojuegoesunodeesosproyectos,necesitaremosprobarrapidamentecadaiteraciónantesdemovernosalasiguiente.

ProcesandounIntentodeAdivinanzaProbemoslo!Laprimeracosaquenecesitamoshacerparanuestrojuegodeadivinanzasespermitiranuestrojugadoringresarunintentodeadivinanza.Colocaestoentusrc/main.rs:

usestd::io;

fnmain(){

println!("Adivinaelnumero!");

println!("Porfavorintroducetucorazonada.");

letmutcorazonada=String::new();

io::stdin().read_line(&mutcorazonada)

.ok()

.expect("Falloalleerlinea");

println!("Tucorazonadafue:{}",corazonada);

}

Hayunmontonaqui!Tratemosdeiratravesdeello,piezaporpieza.

ElLenguajedeProgramacionRust

22ElJuegodelasAdivinanzas

usestd::io;

Necesitaremosrecibirentradadelusuario,paraluegoimprimirelresultadocomosalida.Debidoaestonecesitamoslabibliotecaiodelabibliotecaestandar.Rustsoloimportaunaspocascosasparatodoslosprogramas,esteconjuntodecosassedenomina‘preludio’.Sinoestaenelpreludiotendrásquellamarlodirectamenteatravesdeuse.

fnmain(){

Comohasvistoconanterioridad,lafunciónmain()eselpuntodeentradaatuprograma.Lasintaxisfndeclaraunanuevafunción,los()sindicanquenohayargumentosy{comienzaelcuerpodelafunción.Debidoaquenoincluimosuntipoderetorno,seasumeser()unatuplavacía.

println!("Adivinaelnumero!");

println!("Porfavorintroducetucorazonada.");

Anteriormenteaprendimosqueprintln!()esunamacroqueimprimeunacadenadecaracteresalapantalla.

letmutcorazonada=String::new();

Ahoranosestamosponiendointeresantes!Hayunmontóndecosaspasandoenestapequeñalinea.Laprimeracosasanotaresqueesunasentencialet,usadaparacrearvariables.Tienelaforma:

letfoo=bar;

Estocrearaunanuevavariablellamadafoo,ylaenlazaraalvalorbar.Enmuchoslenguajes,estoesllamadouna‘variable’perolasvariablesdeRusttienenunpardetrucosbajolamanga.

Porejemplo,sonimmutablespordefecto.Esporelloquenuestroejemplousamut:estohaceunbindingmutable,envezdeinmutable.letnosolotomaunnombredelladoizquierdo,letaceptaun‘patrón’.Usaremoslospatronesunpocomastarde.Essuficienteporahorausar:

letfoo=5;//inmutable.

letmutbar=5;//mutable

ElLenguajedeProgramacionRust

23ElJuegodelasAdivinanzas

Ah,//iniciauncomentario,hastaelfinaldelalinea.Rustignoratodoencomentarios

Entoncessabemosqueletmutcorazonadaintroduciráunbindingmutablellamadocorazonada,perotenemosqueveralotroladodel=parasaberaqueestasiendoasociado:String::new().

Stringesuntipodecadenadecaracter,proporcionadoporlabibliotecaestandar.UnStringesunsegmentodetextocodificadoenUTF-8capazdecrecer.

Lasintaxis::new()usa::porqueesuna‘funciónasociada’deuntipoparticular.EsdecirestaasociadaconStringensimismo,envezdeconunainstaciaenparticulardeString.Algunoslenguajesllamanaestoun‘metodoestatico’.

Estafuncionesllamadanew(),porquecreaunnuevoStringvacio.Encontrarasunafunciónnew()enmuchostipos,debidoaqueesunnombrecomúnparalacreacióndeunnuevovalordealguntipo.

Continuemos:

io::stdin().read_line(&mutcorazonada)

.ok()

.expect("Fallolecturadelinea");

Otromonton!Vallamospiezaporpieza.Laprimeralineatienedospartes.Heaquilaprimera:

io::stdin()

Recuerdascomousamosuseenstd::ioenlaprimeralineadenuestroprograma?Ahoraestamosllamandounafunciónasociadaenstd::io.Denohaberusadousestd::io,pudimoshaberescritoestalineacomostd::io::stdin().

Estafunciónenparticularretornaunhandlealaentradaestándardetuterminal.Masespecificamente,unstd::io::Stdin.

Lasiguienteparteusaradichohandleparaobtenerentradadelusuario:

.read_line(&mutcorazonada)

Aqui,llamamoselmetodoread_line()ennuestrohandle.Losmetodossonsimilaresalasfuncionesasociadas,perosoloestandisponiblesenunainstanciaenparticulardeuntipo,envezdeeneltipoensi.Tambiénestamospasandounargumentoaread_line():&mutcorazonada.

ElLenguajedeProgramacionRust

24ElJuegodelasAdivinanzas

Recuerdascuandocreamoscorazonada?Dijimosqueeramutable.Sinembargoread_linenoaceptaunStringcomoargumento:aceptaun&mutString.Rustposeeunacaracteristicallamada‘referencias’(‘references’),lacualpermitetenermultiplesreferenciasaunapiezadedata,deestamanerasereducelanecesidaddecopiado.Lasreferenciassonunacaracteristicacompleja,debidoaqueunodelospuntosdeventamasfuertesdeRustesacercadecuanfácilyseguroesusarreferencias.Porahoranonecesitamossabermuchodeesosdetallesparafinalizarnuestroprograma.Todoloquenecesitamossaberporelmomentoesquealigualquelosbindingsletlasreferenciassoninmutablespordefecto.Comoconsecuencianecesitamosescribir&mutcorazonadaenvezde&corazonada.

Porqueread_line()aceptaunareferenciamutableaunacadenadecaracteres.Sutrabajoestomarloqueelusuarioingresaenlaentradaestandar,ycolocarloenunacadenadecaracteres.Debidoaellotomadichacadenacomoargumento,ydebidoaquedebedeagregarlaentradadelusuario,estenecesitasermutable.

Todavianohemosterminadoconestalinea.Sibienesunasolalineadetexto,essololaprimerapartedeunalinealogicadecódigocompleta:

.ok()

.expect("Fallolecturadelinea");

Cuandollamasaunmetodoconlasintaxis.foo()puedesintroducirunsaltodelineayotroespacio.Estoteayudaadividirlineaslargas.Pudimoshaberescrito:

io::stdin().read_line(&mutcorazonada).ok().expect("Fallolecturadelinea");

Peroesoesmasdifícildeleer.Asíquelohemosdivididoentreslineasparatresllamadasametodo.Yahemoshabladoderead_line(),peroqueacercadeok()yexpect()?Bueno,yamencionamosqueread_line()colocalaentradadelusuarioenel&mutStringqueleproprocionamos.Perotambienretornaunvalor:enestecasounio::Result.RustposeeunnumerodetiposllamadosResultensubibliotecaestandar:unResultgenerico,yversionesespecificasparasub-bibliotecas,comoio::Result.

ElpropositodeesosResultescodificarinformacióndemanejodeerrores.ValoresdeltipoResulttienenmetodosdefinidosenellos.Enestecasoio::Resultposeeunmetodook(),quesetraduceen‘queremosasumirqueestevaloresunvalorexitoso.Sino,descartalainformaciónacercadelerror’.Porquedescartarlainformaciónacercadelerror?,paraunprogramabásico,queremossimplementeimprimirunerrorgenerico,cualquierproblemaquesignifiquequenopodamoscontinuar.Elmetodook()retornaunvalorquetieneotrometododefinitoenel:expect().Elmetodoexpect()tomaelvalorenelcuales

ElLenguajedeProgramacionRust

25ElJuegodelasAdivinanzas

llamadoysinoesunvalorexitoso,hacepanicopanic!conunmensajequelehemosproporcionado.Unpanic!comoestecausaraqueelprogramatengaunasalidaabrupta(crash),mostrandodichomensaje.

Siquitamoslasllamadasaesosdosmetodos,nuestroprogramacompilara,peroobtendremosunaadvertencia:

$cargobuild

Compilingadivinanzasv0.1.0(file:///home/tu/proyectos/adivinanzas)

src/main.rs:10:5:10:44warning:unusedresultwhichmustbeused,#[warn(unused_must_use)]onbydefault

src/main.rs:10io::stdin().read_line(&mutcorazonada);

^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

RustnosadviertequenohemosusadoelvalorResult.Estaadvertenciavienedeunaanotaciónespecialquetieneio::Result.Rustestatratandodedecirtequenohasmanejadounposibleerror.Lamaneracorrectadesuprimirelerrores,enefectoescribirelcódigoparaelmanejodeerroes.Porsuerte,sisoloqueremosterminarlaejecucióndelprogramadehaberunproblema,podemosusarestosdospequeñosmetodos.Sipudieramosrecuperarnosdelerrordealgunamanera,hariamosalgodiferente,perodejemosesoparaunproyectofuturo.

Solonosquedaunalineadeesteprimerejemplo:

println!("Tucorazonadafue:{}",corazonada);

}

Estalineaimprimelacadenadecaracteresenlaqueguardamosnuestraentrada.Los{}ssonmarcadoresdeposición,esporelloquepasamosadivinanzacomoargumento.Dehaberhabidomultiples{}s,debiamoshaberpasadomultiplesargumentos:

letx=5;

lety=10;

println!("xyy:{}y{}",x,y);

Fácil.

Decualquiermodo,eseeseltour.Podemosejecutarloconcargorun:

ElLenguajedeProgramacionRust

26ElJuegodelasAdivinanzas

$cargorun

Compilingguessing_gamev0.1.0(file:///home/tu/proyectos/adivinanzas)

Running`target/debug/adivinanzas`

Adivinaelnumero!

Porfavorintroducetucorazonada.

6

Tucorazonadafue:6

Enhorabuena!Nuestraprimerapartehaterminado:podemosobtenerentradadeltecladoeimprimirladevuelta.

GenerandounnumerosecretoAcontinuaciónnecesitamosgenerarunnumerosecreto.Rusttodavíanoincluyeunafuncionalidaddenumerosaleatoriosenlabibliotecaestándar.Sinembargo,elequipodeRustproveeuncraterand.Un‘crate’esunpaquetedecódigoRust.Nosotroshemosestadoconstruyendoun‘cratebinaro’,elcualesunejecutable.randesun‘cratebiblioteca’,quecontienecodigoaserusadoporotrosprogramas.

UsarcratesexternosesdondeCargorealmentebrilla.Antesquepodamosescribircódigoquehagausoderand,debemosmodificarnuestroarchivoCargo.toml.Abrelo,yagregaestaslineasalfinal:

[dependencies]

rand="0.3.0"

Lasección[dependencies]deCargo.tomlescomolasección[package]:todoloquelesigueespartedeella,hastaquelasiguienteseccióncomience.Cargousalaseccióndedependenciasparasaberencualescratesexternosdependemos,asicomolasversionesrequeridas.Enestecasohemosusadolaversión0.3.0.CargoentiendeVersionadoSemantico,queesunestandarparalasescrituradenumerosdeversión.Siquisieramosusarlaultimaversionpodriamoshaberusado*ounrangodeversiones.LadocumentacióndeCargocontienemasdetalles.

Ahora,sincambiarnadaennuestrocódigo,construyamosnuestroproyecto:

ElLenguajedeProgramacionRust

27ElJuegodelasAdivinanzas

$cargobuild

Updatingregistry`https://github.com/rust-lang/crates.io-index`

Downloadingrandv0.3.8

Downloadinglibcv0.1.6

Compilinglibcv0.1.6

Compilingrandv0.3.8

Compilingadivinanzasv0.1.0(file:///home/tu/proyectos/adivinanzas)

(Podriasverversionesdiferentes,porsupuesto.)

Unmontóndesalidamas!Ahoraquetenemosunadependenciaexterna,Cargodescargadelregistrolasultimasversionesdetodo,locualpuedecopiardatosdesdeCrates.io.Crates.ioesdondelaspersonasdelecosistemaRustpublicansusproyectosopensourceparaqueotroslosusen.

Despuesdeactualizarelregistro,Cargochequeanuestrasdependencias(en[dependencies])ylasdescargadenotenerlastodavía.Enestecasosolodijimosquequeriamosdependerenrand,ytambienobtuvimosunacopiadelibc.Estoesdebidoaqueranddependeasuvezdelibcparafuncionar.Despuesdedescargarlasdependencias,Cargolascompila,paradespuescompilarnuestrocódigo.

Siejecutamoscargobuild,obtendremosunasalidadiferente:

$cargobuild

Asies,nohaysalida!Cargosabequenuestroproyectohasidoconstruido,asicomotodassusdependencias,asiquenonayrazónparahacertodoelprocesootravez.Sinnadaquehacer,simplementeterminalaejecucion.Siabrimossrc/main.rsotravez,hacemosuncambiotrivial,salvamosloscambios,solamenteveriamosunalinea:

$cargobuild

Compilingadivinanzasv0.1.0(file:///home/tu/proyectos/adivinanzas)

Entonces,lehemosdichoaCargoquequeriamoscualquierversión0.3.xderand,yestedescargolaultimaversiónparaelmomentodelaescrituradeestetutorial,v0.3.8.Peroquepasacuandolasiguienteversiónv0.3.9seapublicadaconunimportantebugfix?Sibienrecibirbugfixesesimportante,quepasasi0.3.9contieneunaregresionquerompenuestrocodigo?

LarespuestaaesteproblemaeselarchivoCargo.lock,archivoqueencontrarasentudirectoriodeproyecto.Cuandoconstruyestuproyectoporprimeravez,cargodeterminatodaslasversionesquecoincidencontuscriteriosylasescribeenelarchivoCargo.lock.Cuandoconstruyestuproyectoenelfuturo,CargonotaraqueunarchivoCargo.lock

ElLenguajedeProgramacionRust

28ElJuegodelasAdivinanzas

existe,yusaralasversionesespecificadasenelmismo,envezdehacertodoeltrabajodedeterminarlasversionesotravez.Estotepermitetenerunaconstrucciónreproducibledemaneraautomatica.Enotraspalabras,nosquedaremosen0.3.8hastaquesubamosdeversiondemaneraexplicita,deigualmaneraloharálagenteconlaquehemoscompartidonuestrocódigo,graciasalarchivoCargo.lock.

Peroquepasacuandoqueremosusarv0.3.9?Cargoposeeotrocomando,update,quesetraduceen‘ignoraelbloqueoydeterminatodaslasultimasversionesquecoincidanconloquehemosespecficado.Defuncionaresto,escribeesasversionesalarchivodebloqueoCargo.lock’.Pero,pordefecto,Cargosolobuscaraversionesmayoresa0.3.0ymenoresa0.4.0.Siqueremosmovernosa0.4.x,necesitariamosactualizarelarchivoCargo.tomldirectamente.Cuandolohacemos,lasiguentevezqueejecutemoscargobuild,Cargoactualizaraelindiceyre-evaluaranuestrosrequerimentosacercaderand.

HaymuchomasquedeciracercadeCargoysuecosistema,peroporahora,esoestodoloquenecesitamossaber.Cargohacerealmentefácilreusarbibliotecas,ylosRusterostiendenaescribirproyectospequenosloscualesestanconstruidosporunconjuntodepaquetesmaspequeños.

Hagamosusoahoraderand.Heaquinuestrosiguientepaso:

externcraterand;

usestd::io;

userand::Rng;

fnmain(){

println!("Adivinaelnumero!");

letnumero_secreto=rand::thread_rng().gen_range(1,101);

println!("Elnumerosecretoes:{}",numero_secreto);

println!("Porfavorintroducetucorazonada.");

letmutcorazonada=String::new();

io::stdin().read_line(&mutcorazonada)

.ok()

.expect("Falloalleerlinea");

println!("Tucorazonadafue:{}",corazonada);

}

ElLenguajedeProgramacionRust

29ElJuegodelasAdivinanzas

Laprimeracosaquehemoshechoescambiarlaprimeralinea.Ahoradiceexterncraterand.Debidoaquedeclaramosrandennuestrasección[dependencies],podemosusarexterncrateparahacerlesaberaRustqueestaremoshaciendousoderand.Estoesequivalenteaunuserand;,demaneraquepodamoshacerusodeloqueseadentrodelcraterandatravesdelprefijorand::.

Después,hemosagregadootralineause:userand::Rng.Enunosmomentosestaremoshaciendousodeunmetodo,yestorequierequeRngestedisponibleparaquefuncione.Laideabasicaeslasiguiente:losmetodosestandentrodealgollamado‘traits’(Rasgos),yparaqueelmetodofuncionenecesitaqueeltraitestedisponible.ParamayoresdetallesdirigetealasecciónRasgos(Traits).

Haydoslineasmasenelmedio:

letnumero_secreto=rand::thread_rng().gen_range(1,101);

println!("Elnumerosecretoes:{}",numero_secreto);

Hacemosusodelafunciónrand::thread_rng()paraobtenerunacopiadelgeneradordenumerosaleatorios,elcualeslocalalhilodeejecucionenelcualestamos.Debidoaquehemoshechodisponiblearand::Rngatravesdeuserand::Rng,estetieneunmetodogen_range()disponible.Estemetodoaceptadosargumentos,ygeneraunnumeroaleatorioentreestos.Esinclusivoenellimiteinferior,peroesexclusivoenellimitesuperior,poresonecesitamos1y101paraobtenerunnumeroentreunoycien.

Lasegundalineasoloimprimeelnumerosecreto.Estoesútilmietrasdesarrollamosnuestroprograma,demaneratalquepodamosprobarlo.Estaremoseliminandoestalineaparalaversionfinal.Noesunjuegosiimprimelarespuestajustocuandoloinicias!

Tratadeejecutarelprogramaunaspocasveces:

ElLenguajedeProgramacionRust

30ElJuegodelasAdivinanzas

$cargorun

Compilingadivinanzasv0.1.0(file:///home/tu/proyectos/adivinanzas)

Running`target/debug/adivinanzas`

Adivinaelnumero!

Elnumerosecretoes:7

Porfavorintroducetucorazonada.

4

Tucorazonadafue:4

$cargorun

Running`target/debug/adivinanzas`

Adivinaelnumero!

Elnumerosecretoes:83

Porfavorintroducetucorazonada.

5

Tucorazonadafue:5

Gradioso!Acontinuacion:comparemosnuestraadivinanzaconelnumerosecreto.

ComparandoadivinanzasAhoraquetenemosentradadelusuario,comparemoslaadivinanzaconnuestronumerosecreto.Heaquinuestrosiguientepaso,aunquetodavianofuncionacompletamente:

ElLenguajedeProgramacionRust

31ElJuegodelasAdivinanzas

externcraterand;

usestd::io;

usestd::cmp::Ordering;

userand::Rng;

fnmain(){

println!("Adivinaelnumero!");

letnumero_secreto=rand::thread_rng().gen_range(1,101);

println!("Elnumerosecretoes:{}",numero_secreto);

println!("Porfavorintroducetucorazonada.");

letmutcorazonada=String::new();

io::stdin().read_line(&mutcorazonada)

.ok()

.expect("Falloalleerlinea");

println!("Tucorazonadafue:{}",corazonada);

matchcorazonada.cmp(&numero_secreto){

Ordering::Less=>println!("Muypequeño!"),

Ordering::Greater=>println!("Muygrande!"),

Ordering::Equal=>println!("Hazganado!"),

}

}

Algunaspiezasacá.Laprimeraesotrouse.Hemoshechodisponibleuntipollamadostd::cmp::Ordering.Despues,cinconuevaslineasalfondoquelousan:

matchcorazonada.cmp(&numero_secreto){

Ordering::Less=>println!("Muypequeño!"),

Ordering::Greater=>println!("Muygrande!"),

Ordering::Equal=>println!("Hazganado!"),

}

Elmetodocmp()puedeserllamadoancualquiercosaquepuedasercomparada,estetomaunareferenciaalacosaconlacualquierascomparar.RetornaeltipoOrderingquehicimosdisponibleanteriormente.HemosusadounasentenciamatchparadeterminarexactamentequetipodeOrderinges.Orderingesunenum,abreviacionpara‘enumeration’,lascualeslucendelasiguientemanera:

ElLenguajedeProgramacionRust

32ElJuegodelasAdivinanzas

enumFoo{

Bar,

Baz,

}

Conestadefinición,cualquiercosadetipoFoopuedeserbienseaunFoo::BarounFoo::Baz.Usamosel::paraindicarelespaciodenombresparaunavarianteenumenparticular.

LaenumOrderingtienetresposiblesvariantes:Less,Equal,andGreater(menor,igualymayorrespectivamente).Lasentenciamatchtomaunvalordeuntipo,ytepermitecrearun‘brazo’paracadavalorposible.DebidoaquetenemostresposiblestiposdeOrdering,tenemostresbrazos:

matchguess.cmp(&secret_number){

Ordering::Less=>println!("Muypequeño!"),

Ordering::Greater=>println!("Muygrande!"),

Ordering::Equal=>println!("Hazganado!"),

}

SiesLess,imprimimosToosmall!,siesGreater,Toobig!,ysiesEqual,Hazganado!.matchesrealmenteutil,yesusadoconfercuenciaenRust.

Anteriormentemencionequetodavianofunciona.Pongamosloaprueba:

$cargobuild

Compilingadivinanzasv0.1.0(file:///home/tu/proyectos/adivinanzas)

src/main.rs:28:21:28:35error:mismatchedtypes:

expected`&collections::string::String`,

found`&_`

(expectedstruct`collections::string::String`,

foundintegralvariable)[E0308]

src/main.rs:28matchcorazonada.cmp(&numero_secreto){

^~~~~~~~~~~~~~

error:abortingduetopreviouserror

Couldnotcompile`adivinanzas`.

Oops!Ungranerror.Loprincipalenelesquetenemos‘tiposincompatibles’(‘mismatchedtypes’).Rustposeeunfuerte,sistemadetiposestatico.Sinembargo,tambiéntieneinferenciadetipos.Cuandoescribimosletcorazonada=String::new(),RustfuecapazdeinferirquecorazonadadebiaserunString,yporellononoshizoescribireltipo.Connuestronumero_secreto,hayunnumerodetiposquepuedentenerunvalorentreunoycien:i32,unnumerodetreintaydosbits,u32,unnumerosinsignodetreintaydosbits,oi64unnumerodesesentaycuatrobitsuotros.Hastaahora,esonohaimportado,

ElLenguajedeProgramacionRust

33ElJuegodelasAdivinanzas

debidoaqueRustpordefectousai32.Sinembargo,enestecaso,Rustnosabecomocompararcorazonadaconnumero_secreto.Ambosnecesitanserdelmismotipo.Alafinal,queremosconvertirelStringqueleimoscomoentradaenuntiporealdenumero,paraefectosdelacomparación.Podemoshaceresocontreslineasmas.Heaquinuestronuevoprograma:

externcraterand;

usestd::io;

usestd::cmp::Ordering;

userand::Rng;

fnmain(){

println!("Adivinaelnumero!");

letnumero_secreto=rand::thread_rng().gen_range(1,101);

println!("Elnumerosecretoes:{}",numero_secreto);

println!("Porfavorintroducetucorazonada.");

letmutcorazonada=String::new();

io::stdin().read_line(&mutcorazonada)

.ok()

.expect("Falloalleerlinea");

letcorazonada:u32=corazonada.trim().parse()

.ok()

.expect("Porfavorintroduceunnumero!");

println!("Tucorazonadafue:{}",corazonada);

matchcorazonada.cmp(&numero_secreto){

Ordering::Less=>println!("Muypequeño!"),

Ordering::Greater=>println!("Muygrande!"),

Ordering::Equal=>println!("Hazganado!"),

}

}

Lastresnuevaslineas:

letcorazonada:u32=corazonada.trim().parse()

.ok()

.expect("Porfavorintroduceunnumero!");

ElLenguajedeProgramacionRust

34ElJuegodelasAdivinanzas

Esperaunmomento,penséqueyateniamosunacorazonada?Latenemos,peroRustnospermitesobreescribir(‘shadow’)lacorazonadapreviaconunanueva.Estoesusadoconfrecuenciaenestamismasituación,endondecorazonadaesunString,peroqueremosconvertirloaunu32.Esteshadowingnospermitereusarelnombrecorazonadaenvezdeforzarnosaideardosnombresúnicoscomocorazonada_strycorazonada,uotros.

Estamosasociandocorazonadaaunaexpresiónquelucecomoalgoqueescribimosanteriormente:

guess.trim().parse()

Seguidoporunainvocaciónaok().expect().Aquícorazonadahacereferenciaalaviejaversión,laqueeraunStringqueconteníanuestraentradadeusuarioenella.Elmetodotrim()enlosStringseliminacualquierespacioenblancoalprincipioyalfinaldenuestrascadenasdecaracteres.Estoesimportante,debidoaquetuvimosquepresionarlatecla‘retorno’parasatisfaceraread_line().Estosignificaquesiescribimos5ypresionamos‘retorno’corazonadalucecomoasí:5\n.El\nrepresenta‘nuevalinea’(‘newline’),lateclaenter.trim()sedeshacedeesto,dejandonuestracadenadecaracteressoloconel5.Elmetodoparse()enlascadenascaracteresparseaunacadenadecaracteresenalgúntipodenumero.Debidoaquepuedeparsearunavariedaddenumeros,debemosdarleaRustunapistadeltipoexactodenumeroquedeseamos.Deahílaparteletcorazonada:u32.Losdospuntos(:)despuesdecorazonadaledicenaRustquevamosaanotareltipo.u32esunenterosinsignodetreintaydosbits.Rustposeeunavariedaddetiposnumerointegrados,peronosotroshemosescojidou32.Esunabuenaopciónpordefectoparaunnumeropositivopequeño.

Aligualqueread_line(),nuestrallamadaaparse()podriacausarunerror.QuetalsinuestracadenadecaracterescontieneA����?Nohabríaformadeconvertiresoenunnumero.Esporelloqueharemoslomismoquehicimosconread_line():usarlosmetodosok()yexpect()paraterminarabruptamentesihayalgunerror.

Probemosnuestroprograma!

$cargorun

Compilingadivinanzasv0.1.0(file:///home/tu/proyectos/adivinanzas)

Running`target/adivinanzas`

Adivinaelnumero!

Elnumerosecretoes:58

Porfavorintroducetucorazonada.

76

Tucorazonadafue:76

Muygrande!

ElLenguajedeProgramacionRust

35ElJuegodelasAdivinanzas

Excelente!Puedesverqueinclusoheagregadoespaciosantesdemiintento,yaunasíelprogramadeterminoqueintente76.Ejecutaelprogramaunaspocasveces,yverificaqueadivinarelnumerofunciona,asicomointentarunnumeromuypequeno.

Ahoratenemoslamayoriadeljuegofuncionando,perosolopodemosintentaradivinarunavez.Tratemosdecambiaresoagregandociclos!

IteraciónLapalabraclaveloopnosproporcionauncicloinfinito.Agreguemosla:

Adivinaelnumero!Elnumerosecretoes:58Porfavorintroducetuadivinanza.76Tucorazonadafue:76Muygrande!

ElLenguajedeProgramacionRust

36ElJuegodelasAdivinanzas

externcraterand;

usestd::io;

usestd::cmp::Ordering;

userand::Rng;

fnmain(){

println!("Adivinaelnumero!");

letnumero_secreto=rand::thread_rng().gen_range(1,101);

println!("Elnumerosecretoes:{}",numero_secreto);

loop{

println!("Porfavorintroducetucorazonada.");

letmutcorazonada=String::new();

io::stdin().read_line(&mutcorazonada)

.ok()

.expect("Falloalleerlinea");

letcorazonada:u32=corazonada.trim().parse()

.ok()

.expect("Porfavorintroduceunnumero!");

println!("Hazcorazonada:{}",corazonada);

matchcorazonada.cmp(&numero_secreto){

Ordering::Less=>println!("Muypequeño!"),

Ordering::Greater=>println!("Muygrande!"),

Ordering::Equal=>println!("Hazganado!"),

}

}

}

Pruebalo.Peroespera,noacabamosdeagregaruncicloinfinito?Sip.Recuerdasnuestradiscusiónacercadeparse()?Sidamosunarespuestanonumérica,retornaremos(return)yfinalizaremoslaejecución.Observa:

ElLenguajedeProgramacionRust

37ElJuegodelasAdivinanzas

$cargorun

Compilingadivinanzasv0.1.0(file:///home/tu/proyectos/adivinanzas)

Running`target/adivinanzas`

Adivinaelnumero!

Elnumerosecretoes:59

Porfavorintroducetucorazonada.

45

Tucorazonadafue:45

Muypequeño!

Porfavorintroducetucorazonada.

60

Tucorazonadafue:60

Muygrande!

Porfavorintroducetucorazonada.

59

Tucorazonadafue:59

Hazganado!

Porfavorintroducetucorazonada.

quit

thread'<main>'panickedat'Pleasetypeanumber!'

Ja!quitenefectoterminalaejecución.Asicomocualquierotraentradaquenoseaunnumero.Bueno,estoessuboptimopordecirlomenos.Primerosalgamoscuandoganemos:

ElLenguajedeProgramacionRust

38ElJuegodelasAdivinanzas

externcraterand;

usestd::io;

usestd::cmp::Ordering;

userand::Rng;

fnmain(){

println!("Adivinaelnumero!");

letnumero_secreto=rand::thread_rng().gen_range(1,101);

println!("Elnumerosecretoes:{}",numero_secreto);

loop{

println!("Porfavorintroducetucorazonada.");

letmutcorazonada=String::new();

io::stdin().read_line(&mutcorazonada)

.ok()

.expect("Falloalleerlinea");

letcorazonada:u32=corazonada.trim().parse()

.ok()

.expect("Porfavorintroduceunnumero!");

println!("Tucorazonadafue:{}",corazonada);

matchcorazonada.cmp(&numero_secreto){

Ordering::Less=>println!("Muypequeño!"),

Ordering::Greater=>println!("Muygrande!"),

Ordering::Equal=>{

println!("Hazganado!");

break;

}

}

}

}

Alagregarlalineabreakdespuesdel"Hazganado!",romperemoselciclocuandoganemos.Salirdelciclotambiénsignificasalirdelprograma,debidoaqueeslaultimacosaenmain().Solonosquedaunasolamejoraporhacer:cuandoalguienintroduzcaunvalornonumérico,noqueremosterminarlaejecución,queremossimplementeignorarlo.Podemoshacerlodelasiguientemanera:

ElLenguajedeProgramacionRust

39ElJuegodelasAdivinanzas

externcraterand;

usestd::io;

usestd::cmp::Ordering;

userand::Rng;

fnmain(){

println!("Adivinaelnumero!");

letnumero_secreto=rand::thread_rng().gen_range(1,101);

println!("Elnumerosecretoes:{}",numero_secreto);

loop{

println!("Porfavorintroducetucorazonada.");

letmutcorazonada=String::new();

io::stdin().read_line(&mutcorazonada)

.ok()

.expect("Falloalleerlinea");

letcorazonada:u32=matchcorazonada.trim().parse(){

Ok(num)=>num,

Err(_)=>continue,

};

println!("Tucorazonadafue:{}",corazonada);

matchcorazonada.cmp(&numero_secreto){

Ordering::Less=>println!("Muypequeño!"),

Ordering::Greater=>println!("Muygrande!"),

Ordering::Equal=>{

println!("Hazganado!");

break;

}

}

}

}

Estassonlaslineasquehancambiado:

letcorazonada:u32=matchcorazonada.trim().parse(){

Ok(num)=>num,

Err(_)=>continue,

};

ElLenguajedeProgramacionRust

40ElJuegodelasAdivinanzas

Esasicomopasamosde‘terminarabruptamenteenunerror’a‘efectivamentemanejarelerror’,atravésdelcambiodeok().expect()aunasentenciamatch.ElResultretornadoporparse()esunenumjustocomoOrdering,peroenestecasocadavariantetienedataasociada:Okesexito,yErresunafalla.Cadaunocontienemasinformación:elenteroparseadoenelcasoexitoso,ountipodeerror.EnestecasohacemosmatchenOk(num),elcualasignaelvalorinternodelOkaelnombrenum,yseguidamenteretornaenelladoderecho.EnelcasodeErr,nonosimportaquetipodeerrores,esporelloqueusamos_enlugardeunnombre.Estoignoraelerrorycontinuenosmuevealasiguienteiteracióndelciclo(loop).

Ahoradeberiamosestarbien!Probemos:

$cargorun

Compilingadivinanzasv0.1.0(file:///home/tu/proyectos/adivinanzas)

Running`target/adivinanzas`

Adivinaelnumero!

Elnumerosecretoes:61

Porfavorintroducetucorazonada.

10

Tucorazonadafue:10

Muypequeño!

Porfavorintroducetucorazonada.

99

Tucorazonadafue:99

Muypequeño!

Porfavorintroducetucorazonada.

foo

Porfavorintroducetucorazonada.

61

Tucorazonadafue:61

Hazganado!

Genial!Conunaultimamejora,finalizamoseljuegodelasadvinanzas.Teimaginascuales?Escorrecto,noqueremosimprimirelnumerosecreto.Erabuenoparalaspruebas,peroarruinanuestrojuego.Heaquinuestrocódigofuentefinal:

ElLenguajedeProgramacionRust

41ElJuegodelasAdivinanzas

externcraterand;

usestd::io;

usestd::cmp::Ordering;

userand::Rng;

fnmain(){

println!("Adivinaelnumero!");

letnumero_secreto=rand::thread_rng().gen_range(1,101);

loop{

println!("Porfavorintroducetucorazonada.");

letmutcorazonada=String::new();

io::stdin().read_line(&mutcorazonada)

.ok()

.expect("Falloalleerlinea");

letcorazonada:u32=matchcorazonada.trim().parse(){

Ok(num)=>num,

Err(_)=>continue,

};

println!("Tucorazonadafue:{}",corazonada);

matchcorazonada.cmp(&numero_secreto){

Ordering::Less=>println!("Muypequeño!"),

Ordering::Greater=>println!("Muygrande!"),

Ordering::Equal=>{

println!("Hazganado!");

break;

}

}

}

}

Completado!Enestepunto,hasterminadosatisfactoriamenteeljuegodelasadivinanza!Felicitaciones!

Esteprimerproyectoteensenounmontón:let,match,metodos,funcionesasociadas,usarcratesexternos,ymas.Nuestrosiguienteproyectodemostraraaunmas.

ElLenguajedeProgramacionRust

42ElJuegodelasAdivinanzas

%LaCenadelosFilósofos

Paranuestrosegundoproyecto,echemosunvistazoaunproblemaclásicodeconcurrencia.Sellama‘Lacenadelosfilósofos’.FueoriginalmenteconcebidoporDijkstraen1965,peronosotrosusaremosunaversionligeramenteadaptadadeestepaperporTonyHoareen1985.

Entiemposancestrales,unfilántropoadineradopreparounauniversidadparaalojaracincofilósofoseminentes.Cadafilósofoteniaunahabitaciónenlacualpodíadesempeñarsuactividadprofesionaldelpensamiento:tambiénhabíauncomedorencomún,amobladoconunamesacircular,rodeadaporcincosillas,cadaunaidentificadaconelnombredelfilosofoquesesentabaenella.Losfilósofossesentabanensentidoanti-horarioalrededordelamesa.Alaizquierdadecadafilósofoyacíauntenedordorado,yenelcentrountazóndeespagueti,elcualeraconstantementerellenado.Seesperabaqueunfilósofoemplearalamayoríadesutiempopensando;perocuandosesintieranconhambre,sedirigieraaelcomedortomaraeltenedorqueestabaasuizquierdaylosumieranenelespagueti.Perotaleranaturalezaenredadadelespaguetiqueunsegundotenedorerarequeridoparallevarloalaboca.Elfilósofoporendeteniaquetambiéntomareltenedorasuderecha.Cuandoterminabandebíanbajarambostenedores,levantarsedelasillaycontinuarpensando.Porsupuesto,untenedorpuedeserusadoporunsolofilósofoalavez.Siotrofilósofolodesea,tienequeesperarhastaqueeltenedorestedisponiblenuevamente.

Esteproblemaclásicoexhibealgunoselementosdelaconcurrencia.Larazóndeelloesqueesunasoluciónefectivamentedifícildeimplementar:unaimplementaciónsimplepuedegenerarundeadlock.Porejemplo,consideremosunalgoritmosimplequepodríaresolveresteproblema:

1. Unfilósofotomaeltenedorasuizquierda.2. Despuéstomaeltenedorenasuderecha.3. Come.4. Bajalostenedores.

Ahora,imaginemosestasecuenciadeeventos:

1. Filosofo1comienzaelalgoritmo,tomandoeltenedorasuizquierda.2. Filosofo2comienzaelalgoritmo,tomandoeltenedorasuizquierda.3. Filosofo3comienzaelalgoritmo,tomandoeltenedorasuizquierda.4. Filosofo4comienzaelalgoritmo,tomandoeltenedorasuizquierda.5. Filosofo5comienzaelalgoritmo,tomandoeltenedorasuizquierda.6. ...?Todoslostenedoreshansidotomados,peronadiepuedecomer!

ElLenguajedeProgramacionRust

43LaCenadelosFilósofos

Existendiferentesformasderesolveresteproblema.Teguiaremosatravésdelasolucióndeestetutorial.Porahora,comencemosmodelandoelproblema.Empecemosconlosfilósofos:

structFilosofo{

nombre:String,

}

implFilosofo{

fnnew(nombre:&str)->Filosofo{

Filosofo{

nombre:nombre.to_string(),

}

}

}

fnmain(){

letf1=Filosofo::new("JudithButler");

letf2=Filosofo::new("GillesDeleuze");

letf3=Filosofo::new("KarlMarx");

letf4=Filosofo::new("EmmaGoldman");

letf5=Filosofo::new("MichelFoucault");

}

Acá,creamosunaestructura(struct)pararepresentarunfilósofo.Porahoraelnombreestodoloquenecesitamos.ElegimoseltipoStringparaelnombre,envezde&str.Generalmentehablando,trabajarcontipoqueesdueño(poseepertenencia)desudataesmasfácilquetrabajarconunoqueusereferencias.

Continuemos:

#structFilosofo{

#nombre:String,

#}

implFilosofo{

fnnew(nombre:&str)->Filosofo{

Filosofo{

nombre:nombre.to_string(),

}

}

}

EstebloqueimplnospermitedefinircosasenestructurasFilosofo.Enestecasoestamosdefiniendouna‘funciónasociada’llamadanew.Laprimeralinealuceasí:

ElLenguajedeProgramacionRust

44LaCenadelosFilósofos

#structFilosofo{

#nombre:String,

#}

#implFilosofo{

fnnew(nombre:&str)->Filosofo{

#Filosofo{

#nombre:nombre.to_string(),

#}

#}

#}

Recibimosunargumento,nombre,detipo&str.Unareferenciaaotracadenadecaracteres.EstaretornaunainstanciadenuestraestructuraFilosofo.

#structFilosofo{

#nombre:String,

#}

#implFilosofo{

#fnnew(nombre:&str)->Filosofo{

Filosofo{

nombre:nombre.to_string(),

}

#}

#}

LoanteriorcreaunnuevoFilosofo,yasignanuestroargumentonombreaelcamponombre.Noaelargumentoensimismo,debidoaquellamamos.to_string()enel.Locualcreaunacopiadelacadenaalaqueapuntanuestro&str,ynosdaunnuevoString,queesdeltipodelcamponombredeFilosofo.

PorquenoaceptarunStringdirectamente?Esmasfácildellamar.SirecibiéramosunStringperoquiennosllamatuvieseun&strellosseveríanenlaobligacióndellamar.to_string()desulado.Ladesventajadeestaflexibilidadesquesiemprehacemosunacopia.Paraestepequeñoprograma,estonoesparticularmenteimportante,yquesabemosqueestaremosusandocadenascortasdecualquiermodo.

Unaultimacosasquehabrásnotado:solodefinimosunFilosofo,ynoparecemoshacernadaconel.Rustesunlenguaje‘basadoenexpresiones’,loquesignificaquecasicualquiercosaenRustesunaexpresiónqueretornaunvalor.Estoesciertoparalasfuncionestambién,laultimaexpresiónesretornadaautomáticamente.DebidoaquecreamosunnuevoFilosofocomolaultimaexpresióndeestafunción,terminamosretornándolo.

ElLenguajedeProgramacionRust

45LaCenadelosFilósofos

Elnombrenew(),noesnadaespecialparaRust,peroesunaconvenciónparafuncionesquecreannuevasinstanciasdeestructuras.Antesquehablemosdelporque,echamosunvistazoamain()otravez:

#structFilosofo{

#nombre:String,

#}

#

#implFilosofo{

#fnnew(nombre:&str)->Filosofo{

#Filosofo{

#nombre:nombre.to_string(),

#}

#}

#}

#

fnmain(){

letf1=Filosofo::new("JudithButler");

letf2=Filosofo::new("GillesDeleuze");

letf3=Filosofo::new("KarlMarx");

letf4=Filosofo::new("EmmaGoldman");

letf5=Filosofo::new("MichelFoucault");

}

Acá,creamoscincovariablesconcinconuevosfilósofos.Estossonmiscincofavoritos,peropuedessubstituirlosconquienesprefieras.Denohaberdefinidolafunciónnew(),main()luciríaasí:

#structFilosofo{

#nombre:String,

#}

fnmain(){

letf1=Filosofo{nombre:"JudithButler".to_string()};

letf2=Filosofo{nombre:"GillesDeleuze".to_string()};

letf3=Filosofo{nombre:"KarlMarx".to_string()};

letf4=Filosofo{nombre:"EmmaGoldman".to_string()};

letf5=Filosofo{nombre:"MichelFoucault".to_string()};

}

Unpocomasruidoso.Usarnewtienetambiénposeeotrasventajas,peroinclusoenestesimplecasoterminaporserdemejorutilidad.

Ahoraquetenemoslobásicoensulugar,hayunnumerodemanerasenlascualespodemosatacarelproblemamasamplio.Amimegustacomenzarporelfinal:creemosunaformaparaquecadafilosofopuedafinalizardecomer.Comounpasopequeño,hagamosunmétodo,yluegoiteremosatravésdetodoslosfilósofosllamándolo:

ElLenguajedeProgramacionRust

46LaCenadelosFilósofos

structFilosofo{

nombre:String,

}

implFilosofo{

fnnew(nombre:&str)->Filosofo{

Filosofo{

nombre:nombre.to_string(),

}

}

fncomer(&self){

println!("{}hafinalizadodecomer.",self.nombre);

}

}

fnmain(){

letfilosofos=vec![

Filosofo::new("JudithButler"),

Filosofo::new("GillesDeleuze"),

Filosofo::new("KarlMarx"),

Filosofo::new("EmmaGoldman"),

Filosofo::new("MichelFoucault"),

];

forfin&filosofos{

f.comer();

}

}

Primeroveamosamain().Enlugardetenercincovariablesindividualesparanuestrosfilósofos,creamosunVec<T>.Vec<T>esllamadotambiénun‘vector’,yesunarreglocapazdecrecer.Despuésusamosunciclo[for][for]paraiteraratravésdelvector,obteniendounreferenciaacadafilosofoalavez.

Enelcuerpodelbucle,llamamosf.comer();,queestadefinidocomo:

fncomer(&self){

println!("{}hafinalizadodecomer.",self.nombre);

}

EnRust,losmétodosrecibenunparámetroexplícitoself.Esporelloquecomer()esunmétodoynewesunafunciónasociada:new()notieneself.Paranuestraprimeraversiondecomer(),soloimprimimoselnombredelfilósofo,ymencionamosquehafinalizadodecomer.Ejecutaresteprogramadebergenerarlasiguientesalida:

ElLenguajedeProgramacionRust

47LaCenadelosFilósofos

JudithButlerhafinalizadodecomer.

GillesDeleuzehafinalizadodecomer.

KarlMarxhafinalizadodecomer.

EmmaGoldmanhafinalizadodecomer.

MichelFoucaulthafinalizadodecomer.

Muyfácil,todoshanterminadodecomer!Peronohemosimplementadoelproblemarealtodavía,asíqueaunnoterminamos!

Acontinuación,nosoloqueremossolofinalicendecomer,sinoqueefectivamentecoman.Heaquílasiguienteversión:

usestd::thread;

structFilosofo{

nombre:String,

}

implFilosofo{

fnnew(nombre:&str)->Filosofo{

Filosofo{

nombre:nombre.to_string(),

}

}

fncomer(&self){

println!("{}estacomiendo.",self.nombre);

thread::sleep_ms(1000);

println!("{}hafinalizadodecomer.",self.nombre);

}

}

fnmain(){

letfilosofos=vec![

Filosofo::new("JudithButler"),

Filosofo::new("GillesDeleuze"),

Filosofo::new("KarlMarx"),

Filosofo::new("EmmaGoldman"),

Filosofo::new("MichelFoucault"),

];

forfin&filosofos{

f.comer();

}

}

Solounospocoscambios.Analicémoslosparteporparte.

ElLenguajedeProgramacionRust

48LaCenadelosFilósofos

usestd::thread;

usehacedisponiblesnombresennuestroámbito(scope).Comenzaremosausarelmodulothreaddelabibliotecaestándar,yesporelloquenecesitamoshaceruseenel.

fncomer(&self){

println!("{}estacomiendo.",self.nombre);

thread::sleep_ms(1000);

println!("{}hafinalizadodecomer.",self.nombre);

}

Ahoraestamosimprimiendodosmensajes,conunsleep_ms()enelmedio.Locualsimularaeltiempoquetardaunfilosofoencomer.

Siejecutasesteprograma,deberiasvercomeracadafilosofoalavez:

JudithButlerestacomiendo.

JudithButlerhafinalizadodecomer.

GillesDeleuzeestacomiendo.

GillesDeleuzehafinalizadodecomer.

KarlMarxestacomiendo.

KarlMarxhafinalizadodecomer.

EmmaGoldmanestacomiendo.

EmmaGoldmanhafinalizadodecomer.

MichelFoucaultestacomiendo.

MichelFoucaulthafinalizadodecomer.

Excelente!Estamosavanzando.Solohayundetalle:noestamosoperandodemaneraconcurrente,locualespartecentraldenuestroproblema!

Parahaceranuestrosfilósofoscomerdemaneraconcurrente,necesitamoshacerunpequeñocambio.

Heaquilasiguienteiteración:

ElLenguajedeProgramacionRust

49LaCenadelosFilósofos

usestd::thread;

structFilosofo{

nombre:String,

}

implFilosofo{

fnnew(nombre:&str)->Filosofo{

Filosofo{

nombre:nombre.to_string(),

}

}

fncomer(&self){

println!("{}estacomiendo.",self.nombre);

thread::sleep_ms(1000);

println!("{}hafinalizadodecomer.",self.nombre);

}

}

fnmain(){

letfilosofos=vec![

Filosofo::new("JudithButler"),

Filosofo::new("GillesDeleuze"),

Filosofo::new("KarlMarx"),

Filosofo::new("EmmaGoldman"),

Filosofo::new("MichelFoucault"),

];

lethandles:Vec<_>=filosofos.into_iter().map(|f|{

thread::spawn(move||{

f.comer();

})

}).collect();

forhinhandles{

h.join().unwrap();

}

}

Todoloquehemoshechoescambiarelcicloenmain(),yagregadounsegundo!Esteeselprimercambio:

lethandles:Vec<_>=filosofos.into_iter().map(|f|{

thread::spawn(move||{

f.comer();

})

}).collect();

ElLenguajedeProgramacionRust

50LaCenadelosFilósofos

Aunasísonsolocincolineas,soncincodensaslineas.Analicemosporpartes.

lethandles:Vec<_>=

Introducimosunanuevavariable,llamadahandles.Lehemosdadoestenombreporquecrearemosalgunosnuevoshilos,queresultaranenalgunoshandles(agarradores,manillas)aesosdichoshilosloscualesnospermitiráncontrolarsuoperación.Necesitamosanotareltipoexplícitamente,debidoaalgoqueharemosreferenciamasadelante.El_esunmarcadordeposiciónparauntipo.Estamosdiciendo“handlesesunvectordealgo,perotu,Rust,puedesdeterminarqueesesealgo.”

filosofos.into_iter().map(|f|{

Tomamosnuestralistadefilósofosyllamamosinto_iter()enella.Estocreauniteradorqueseadueña(tomapertenencia)decadafilosofo.Necesitamoshacerestoparapoderpasarlosfilósofosanuestroshilos.Luegotomamoseseiteradoryllamamosmapenel,métodoquetomaunclosurecomoargumentoyllamadichoclosureencadaunodeloselementosalavez.

thread::spawn(move||{

f.comer();

})

Esaquídondelaconcurrenciaocurre.Lafunciónthread::spawntomaunclosurecomoargumentoyejecutaeseclosureenunnuevohilo.Elclosurenecesitaunaanotaciónextra,move,paraindicarqueelclosurevaaadueñarsedelosvaloresqueestacapturando.Principalmente,lavariablefdelafunciónmap.

Dentrodelhilo,todoloquehacemosesllamaracomer();enf.

}).collect();

Finalmente,tomamoselresultadodetodosesasllamadasamapyloscoleccionamos.collect()losconvertiráenunacoleccióndealgunatipo,queeselporqueanotamoseltipoderetorno:queremosunVec<T>.Loselementossonlosvaloresretornadosdelasllamadasathread::spawn,quesonhandlesaesoshilos.Whew!

forhinhandles{

h.join().unwrap();

}

ElLenguajedeProgramacionRust

51LaCenadelosFilósofos

Alfinaldemain(),iteramosatravésdeloshandlesllamandojoin()enellos,locualbloquealaejecuciónhastaqueelhilohayacompletadosuejecución.Estoaseguraqueelhilocompletesuejecuciónantesqueelprogramatermine.

Siejecutasesteprograma,verasquelosfilósofoscomensinorden!Tenemosmulti-hilos!

GillesDeleuzeestacomiendo.

GillesDeleuzehafinalizadodecomer.

EmmaGoldmanestacomiendo.

EmmaGoldmanhafinalizadodecomer.

MichelFoucaultestacomiendo.

JudithButlerestacomiendo.

JudithButlerhafinalizadodecomer.

KarlMarxestacomiendo.

KarlMarxhafinalizadodecomer.

MichelFoucaulthafinalizadodecomer.

Peroqueacercadelostenedoresnoloshemosmodeladodeltodotodavía.

Parahacerlo,creemosunnuevostruct:

usestd::sync::Mutex;

structMesa{

tenedores:Vec<Mutex<()>>,

}

EstaMesacontieneunvectordeMutexes.Unmutexesunaformadecontrolarconcurrencia,solounhilopuedeaccederelcontenidoalavez.Estaeslaexactamentelapropiedadquenecesitamosparanuestrostenedores.Usamosunaduplavacía,(),dentrodelmutex,debidoaquenovamosausarelvalor,solonosaferraremosael.

ModifiquemoselprogramaparahacerusodeMesa:

usestd::thread;

usestd::sync::{Mutex,Arc};

structFilosofo{

nombre:String,

izquierda:usize,

derecha:usize,

}

implFilosofo{

fnnew(nombre:&str,izquierda:usize,derecha:usize)->Filosofo{

Filosofo{

nombre:nombre.to_string(),

izquierda:izquierda,

ElLenguajedeProgramacionRust

52LaCenadelosFilósofos

derecha:derecha,

}

}

fncomer(&self,mesa:&Mesa){

let_izquierda=mesa.tenedores[self.izquierda].lock().unwrap();

let_derecha=mesa.tenedores[self.derecha].lock().unwrap();

println!("{}estacomiendo.",self.nombre);

thread::sleep_ms(1000);

println!("{}hafinalizadodecomer.",self.nombre);

}

}

structMesa{

tenedores:Vec<Mutex<()>>,

}

fnmain(){

letmesa=Arc::new(Mesa{tenedores:vec![

Mutex::new(()),

Mutex::new(()),

Mutex::new(()),

Mutex::new(()),

Mutex::new(()),

]});

letfilosofos=vec![

Filosofo::new("JudithButler",0,1),

Filosofo::new("GillesDeleuze",1,2),

Filosofo::new("KarlMarx",2,3),

Filosofo::new("EmmaGoldman",3,4),

Filosofo::new("MichelFoucault",0,4),

];

lethandles:Vec<_>=filosofos.into_iter().map(|f|{

letmesa=mesa.clone();

thread::spawn(move||{

f.comer(&mesa);

})

}).collect();

forhinhandles{

h.join().unwrap();

}

}

Muchoscambios!Sinembargo,conestaiteración,hemosobtenidounprogramafuncional.Veamoslosdetalles:

ElLenguajedeProgramacionRust

53LaCenadelosFilósofos

usestd::sync::{Mutex,Arc};

Usaremosotraestructuradelpaquetestd::sync:Arc<T>.

Hablaremosmasacercadeellacuandolausemos.

structFilosofo{

nombre:String,

izquierda:usize,

derecha:usize,

}

VamosanecesitaragregardoscamposmasanuestraestructuraFilosofo.Cadafilosofotendrádostenedores:eldelaizquierda,yeldeladerecha.Usaremoseltipousizeparaindicarlos,debidoaqueesteeseltipoconelcualseindexanlosvectores.EstosdosvaloresseránindicesenlostenedoresquenuestraMesaposee.

fnnew(nombre:&str,izquierda:usize,derecha:usize)->Filosofo{

Filosofo{

nombre:nombre.to_string(),

izquierda:izquierda,

derecha:derecha,

}

}

Ahoranecesitamosconstruiresosvaloresizquierdayderecha,demaneraquepodamosagregarlosanew().

fncomer(&self,mesa:&Mesa){

let_izquierda=mesa.tenedores[self.izquierda].lock().unwrap();

let_derecha=mesa.tenedores[self.derecha].lock().unwrap();

println!("{}estacomiendo.",self.nombre);

thread::sleep_ms(1000);

println!("{}hafinalizadodecomer.",self.nombre);

}

Tenemosdosnuevaslineas,tambiénhemosagregadounargumento,mesa.AccedemosalalistadetenedoresdelaMesa,ydespuésusamosself.izquierdayself.derechaparaaccederaltenedorenunindiceenparticular.EsonosdaaccesoalMutexeneseindice,endondellamamoslock().Sielmutexestasiendoaccedidoactualmenteporalguienmas,nosbloquearemoshastaqueestedisponible.

ElLenguajedeProgramacionRust

54LaCenadelosFilósofos

Lallamadaalock()puedefallar,ysilohace,queremosterminarabruptamente.Enestecasoelerrorquepuedeocurriresqueelmutexeste‘envenenado’(‘poisoned’),queesloqueocurrecuandoelhilohacepánicomientraselmantieneelbloqueo.Debidoaqueestonodeberíaocurrir,simplementeusamosunwrap().

Otracosaextrañaacercadeestalineas:hemosnombradolosresultados_izquierdaand_derecha.Quehayconesesub-guion?Bueno,enrealidadnoplaneamosusarelvalordentrodelbloqueo.Soloqueremosadquirirlo.Aconsecuencia,Rustnosadvertiráquenuncausamoselvalor.Atravésdelusodelsub-guionledecimosaRustqueesloquequisimos,deesamaneranogeneraralaadvertencia.

Queacercadesoltarelbloqueo?,Bueno,estoocurrirácuando_izquierday_derechasalgandeámbito,automáticamente.

letmesa=Arc::new(Mesa{tenedores:vec![

Mutex::new(()),

Mutex::new(()),

Mutex::new(()),

Mutex::new(()),

Mutex::new(()),

]});

Acontinuación,enmain(),creamosunanuevaMesaylaenvolvemosenunArc<T>.‘arc’provienede‘atomicreferencecount’(cuentadereferenciasatómica),necesitamoscompartirnuestraMesaentremultipleshilos.Amediaquelacompartimos,lacuentadereferenciassubirá,ycuandocadahilotermine,irabajando.

letfilosofos=vec![

Filosofo::new("JudithButler",0,1),

Filosofo::new("GillesDeleuze",1,2),

Filosofo::new("KarlMarx",2,3),

Filosofo::new("EmmaGoldman",3,4),

Filosofo::new("MichelFoucault",0,4),

];

NecesitamospasarnuestrosvaloresizquierdaandderechaalosconstructoresdenuestrosFilosofos.Perohayundetallemasaquí,yesmuyimportante.Siobservasalpatrón,esconsistentehastaelfinal,MonsieurFoucaultdebetener4,0comoargumentos,peroenvezdeestotiene0,4.Estoesloqueprevienedeadlocks,enefecto:unodelosfilósofoseszurdo!Esaesunaformaderesolverelproblema,yenmiopinion,eslamassimple.

ElLenguajedeProgramacionRust

55LaCenadelosFilósofos

lethandles:Vec<_>=filosofos.into_iter().map(|f|{

letmesa=mesa.clone();

thread::spawn(move||{

f.comer(&mesa);

})

}).collect();

Finalmente,dentrodenuestrociclomap()/collect(),llamamosmesa.clone().Elmétodoclone()enArc<T>esloqueincrementalacuentadereferencias,ycuandosaledeámbito,ladecrementa.Notarasquepodemosintroducirunanuevavariablemesa,yestasobreescribirá(shadow)laanterior.Estoesfrecuentementeusadodemanerataldenotenerqueinventardosnombresúnicos.

Contodoesto,nuestroprogramafunciona!Solodosfilosofopuedencomerenunmomentodadoyenconsecuenciatendrássalidaseveraasí:

GillesDeleuzeestacomiendo.

EmmaGoldmanestacomiendo.

EmmaGoldmanhafinalizadodecomer.

GillesDeleuzehafinalizadodecomer.

JudithButlerestacomiendo.

KarlMarxestacomiendo.

JudithButlerhafinalizadodecomer.

MichelFoucaultestacomiendo.

KarlMarxhafinalizadodecomer.

MichelFoucaulthafinalizadodecomer.

Felicitaciones!HazimplementadounproblemaclásicodeconcurrenciaenRust.

ElLenguajedeProgramacionRust

56LaCenadelosFilósofos

%RustDentrodeOtrosLenguajes

Paranuestrotercerproyecto,elegiremosalgoquedemuestraunadelasmayoresfortalezasdeRust:laausenciadeunentornodeejecución.

Amedidaquelasorganizacionescrecen,estasdemaneraprogresiva,hacenusodeunamultituddelenguajesdeprogramación.Diferenteslenguajesdeprogramaciónposeendiferentesfortalezasydebilidades,yunaarquitecturapoliglotapermiteusarunlenguajeenparticulardondesusfortalezashagansentidoyotrolenguajeseadébil.

Unareamuycomúnendondemuchoslenguajesdeprogramaciónsondébileseselperformanceentiempodeejecución.Frecuentemente,usarunlenguajequeeslento,peroofrecemayorproductividadparaelprogramador,esunequilibrioquevalelapena.Paraayudaramitigaresto,dichoslenguajesproveenunamaneradeescribirunapartedetusistemaenCyluegollamaresecódigocomosihubiesesidoescritoenunlenguajedemasaltonivel.Estafacilidadesdenominada‘interfazdefuncionesforáneas’(‘foreignfunctioninterface’),comúnmenteabreviandoa‘FFI’.

RustposeesoporteparaFFIenambasdirecciones:puedellamarcódigoenCdemanerafácil,perocrucialmentepuedeserllamadotanfácilmentecomoC.Combinadoconlaausenciadeunrecolectordebasuraybajosrequerimientosentiempodeejecución,RustesuncandidatoparaserembebidodentrodeotroslenguajescuandonecesitesesosooKmhextra.

ExisteuncapitulocompletodedicadoaFFIysusdetallesenotrapartedellibro,peroenestecapitulo,examinaremoselusoparticulardeFFIconejemplosenRuby,Python,yJavaScript.

ElproblemaExistenmuchosproblemasquepodríamoshaberescogido,peroelegiremosunejemploenelcualRusttieneunaventajaclaraporencimadeotroslenguajes:computaciónnuméricaehilos.

Muchoslenguajes,enhonoralaconsistencia,colocannúmerosenelmontículo,envezdeenlapila.Especialmenteenlenguajesenfocadosenprogramaciónorientadaaobjetosyelusodeunrecolectordebasura,laasignacióndememoriadesdeelmontículoeselcomportamientopordefecto.Algunasvecesoptimizacionespuedencolocarciertosnúmerosenlapila,peroenvezdeconfiarenunoptimizadorpararealizarestetrabajo,podríamosquererasegurarnosquesiempreestemosusandonumeroprimitivosenvezdealguntipodeobjetos.

ElLenguajedeProgramacionRust

57RustdentrodeotrosLenguajes

Segundo,muchoslenguajesposeenun‘bloqueoglobaldelinterprete’(‘globalinterpreterlock’)(GIL),quelimitalaconcurrenciaenmuchassituaciones.Estoeshechoenelnombredelaseguridadlocualesunefectopositivo,perolimitalacantidaddetrabajoquepuedeserllevadoacabodemaneraconcurrente,locualesungrannegativo.

Paraenfatizarestos2aspectos,crearemosunpequeñoproyectoqueusaestosdosaspectosengranmedida.DebidoaqueelfocodelejemploesembeberRustenotroslenguajes,envezdeelproblemaensimismo,usaremosunejemplodejuguete:

Iniciadiezhilos.Dentrodecadahilo,cuentadesdeunohastacincomillones.Despuésquetodosloshiloshayanfinalizado,imprime"completado!".

Heescogidocincomillonesbasadoenmicomputadorenparticular.HeaquíunejemplodeestecódigoenRuby:

threads=[]

10.timesdo

threads<<Thread.newdo

count=0

5_000_000.timesdo

count+=1

end

end

end

threads.each{|t|t.join}

puts"completado!"

Intentaejecutaresteejemplo,yescogeunnumeroquecorraporunossegundos.Dependiendoenelhardwaredetucomputador,tendrásqueincrementarodecrementarelnumero.

Enmisistema,ejecutaresteprogramatoma2.156.Siusoalgunatipodeherramientademonitoreodeprocesos,comotop,puedoverquesolousaunnúcleoenmimaquina.ElGILpresentehaciendosutrabajo.

Sibienesciertoqueesteenunprogramasintético,unopodríaimaginarmuchosproblemassimilaresaesteenelmundoreal.Paranuestrospropósitos,levantarunospocoshilosyocuparlosrepresentaunaespeciedecomputaciónparalelaycostosa.

UnabibliotecaRust

ElLenguajedeProgramacionRust

58RustdentrodeotrosLenguajes

EscribamosesteproblemaenRust.Primero,creemosunproyectonuevoconCargo:

$cargonewembeber

$cdembeber

EsteprogramaesfácildeescribirenRust:

usestd::thread;

fnprocesar(){

lethandles:Vec<_>=(0..10).map(|_|{

thread::spawn(||{

letmut_x=0;

for_in(0..5_000_000){

_x+=1

}

})

}).collect();

forhinhandles{

h.join().ok().expect("Nosepudounirunhilo!");

}

}

Algodeestodebelucirfamiliaraejemplosanteriores.Iniciamosdiezhilos,colectandolosenunvectorhandles.Dentrodecadahilo,iteramoscincomillonesdeveces,agregandounoa_xencadaiteración.Porqueelsub-guion?Bueno,siloremovemosyluegocompilamos:

$cargobuild

Compilingembeberv0.1.0(file:///Users/goyox86/Code/rust/embeber)

src/lib.rs:3:1:16:2warning:functionisneverused:`procesar`,#[warn(dead_code)]onbydefault

src/lib.rs:3fnprocesar(){

src/lib.rs:4lethandles:Vec<_>=(0..10).map(|_|{

src/lib.rs:5thread::spawn(||{

src/lib.rs:6letmutx=0;

src/lib.rs:7for_in(0..5_000_000){

src/lib.rs:8x+=1

...

src/lib.rs:6:17:6:22warning:variable`x`isassignedto,butneverused,#[warn(unused_variables)]onbydefault

src/lib.rs:6letmutx=0;

^~~~~

Laprimeraadvertenciaesdebidoesaconsecuenciadeestarconstruyendounabiblioteca.Situviéramosunapruebaparaestafunción,laadvertenciadesaparecería.Peroporahoranuncaesllamada.

ElLenguajedeProgramacionRust

59RustdentrodeotrosLenguajes

Lasegundaestarelacionadaaxversus_x.Comoproductodequeefectivamentenohacemosnadaconxobtenemosunaadvertencia.Eso,ennuestrocaso,estaperfectamentebien,puestoquequeremosdesperdiciarciclosdeCPU.Usandounsub-guióndeprefijoeliminamoslaadvertencia.

Finalmente,hacemosjoinencadaunodeloshilos.

Hastaelmomento,sinembargo,esunabibliotecaRust,ynoexponenadaquepuedaserllamadodesdeC.Siquisiéramosconectarlaconotrolenguaje,ensuestadoactual,nofuncionaria.Solonecesitamoshacerunospequeñoscambiosparaarreglarlo.Loprimeroesmodificarelprincipiodenuestrocódigo:

#[no_mangle]

pubexternfnprocesar(){

Debemosagregarunnuevoatributo,no_mangle.CuandocreamosunabibliotecaRust,estecambiaelnombredelafunciónenlasalidacompilada.Lasrazonesdeestoescapandelalcancedeestetutorial,peroparaqueotroslenguajespuedansabercomollamaralafunción,debemosevitarqueelcompiladorcambieelnombreenlasalidacompilada.Esteatributodesactivaesecomportamiento.

Elotrocambioeselpubextern.Elpubsignificaqueestafunciónpuedeserllamadadesdeafueradeestemodulo,yelexterndicequeestapuedeserllamadadesdeC.Esoestodo!Nomuchoscambios.

LasegundacosaquenecesitamoshacerescambiarunaconfiguraciónennuestroCargo.toml.Agregaestoalfinal:

[lib]

name="embeber"

crate-type=["dylib"]

EstaslineasleinformanaRustquequeremoscompilarnuestrabibliotecaenunabibliotecadinámicaestándar.Rustcompilaun‘rlib’,unformatoespecificodeRust.

Ahoraconstruyamoselproyecto:

$cargobuild--release

Compilingembeberv0.1.0(file:///Users/goyox86/Code/rust/embeber)

Hemoselegidocargobuild--release,locualconstruyeelproyectoconoptimizaciones.Queremosquesealomasrápidoposible!Puedesencontrarlasalidadelabibliotecaentarget/release:

ElLenguajedeProgramacionRust

60RustdentrodeotrosLenguajes

$lstarget/release/

builddepsexampleslibembeber.dylibnative

Esalibembeber.dylibesnuestrabibliotecade‘objetoscompartidos’.PodemosusarestabibliotecacomocualquierbibliotecadeobjetoscompartidoescritaenC!Comonota,estapodríaserlibembeber.soolibembeber.dll,dependiendolaplataforma.

AhoraquetenemosnuestrabibliotecaRust,usémosladesdeRuby.

RubyCreaunarchivoembeber.rbdentrodenuestroproyecto,ycolocaestodentro:

require'ffi'

moduleHola

extendFFI::Library

ffi_lib'target/release/libembeber.dylib'

attach_function:procesar,[],:void

end

Hola.procesar

puts'completado!'

Antesdequepodamosejecutarlo,necesitamosinstalarlagemaffi:

$geminstallffi#estopuedenecesitarsudo

Fetching:ffi-1.9.8.gem(100%)

Buildingnativeextensions.Thiscouldtakeawhile...

Successfullyinstalledffi-1.9.8

Parsingdocumentationforffi-1.9.8

Installingridocumentationforffi-1.9.8

Doneinstallingdocumentationforffiafter0seconds

1geminstalled

Finalmente,intentemosejecutarlo:

$rubyembeber.rb

completado!

$

ElLenguajedeProgramacionRust

61RustdentrodeotrosLenguajes

Whoa,esofuerápido!Enmisistema,tomo0.086segundos,adiferenciadelosdossegundosquelaversionenRubypuro.AnalicemosestecódigoRuby:

require'ffi'

Primeronecesitamosrequerirlagemaffi.NospermiteinteractuarconunabibliotecaRustcomounabibliotecaenC.

moduleHola

extendFFI::Library

ffi_lib'target/release/libembeber.dylib'

ElmoduloHolaesusadoparaadjuntarlasfuncionesnativasdelabibliotecacompartida.Dentro,extendemoselmoduloFFI::Libraryyluegollamamoselmétodoffi_libparacargarnuestrabibliotecadeobjetoscompartidos.Simplementepasamoslarutaenlacualnuestrabibliotecaestaalmacenada,lacual,comovimosanteriormente,estarget/release/libembeber.dylib.

attach_function:procesar,[],:void

Elmétodoattach_functionesproporcionadoporlagemaFFI.Esloqueconectanuestrafunciónprocesar()enRustaunmétodoenRubyconelmismonombre.Debidoaqueprocesar()norecibeargumentos,elsegundoparámetroesunarreglovacío,yyaquenoretornanada,pasamos:voidcomoargumentofinal.

Hola.procesar

EstaeslallamadaaRust.Lacombinacióndenuestromoduloylallamadaaattach_functionhanconfiguradotodo.SevecomounmétodoRubyperoesenrealidadcódigoRust!

puts'completado!'

Finalmente,ycomorequerimientodenuestroproyecto,imprimimoscompletado!.

Esoestodo!Comohemosvisto,hacerunpuenteentrelosdoslenguajesesrealmentefácil,ynoscompramuchoperformance.

Acontinuación,probemosPython!

ElLenguajedeProgramacionRust

62RustdentrodeotrosLenguajes

PythonCreaunarchivoembeber.pyenestedirectorio,ycolocaestoenel:

fromctypesimportcdll

lib=cdll.LoadLibrary("target/release/libembeber.dylib")

lib.procesar()

print("completado!")

Aunmasfácil!Usamoscdlldelmoduloctypes.UnallamadarápidaaLoadLibrarydespués,yluegopodemosllamarprocesar().

Enmisistema,toma0.017segundos.Rápidillo!

Node.jsNodenoesunlenguaje,peroesactualmentelaimplementacióndeJavascriptdominantedelladodelservidor.

ParahacerFFIconNode,primeronecesitamosinstalarlabiblioteca:

$npminstallffi

Despuésdequeesteinstalada,podemosusarla:

varffi=require('ffi');

varlib=ffi.Library('target/release/libembeber',{

'procesar':['void',[]]

});

lib.procesar();

console.log("completado!");

LucemasparecidoalejemploRubyquealdePython.Usamoselmoduloffiparaobteneraccesoaffi.Library(),lacualnospermitecargarnuestrabibliotecadeobjetoscompartidos.Necesitamosanotareltipoderetornoylostiposdelosargumentosdela

ElLenguajedeProgramacionRust

63RustdentrodeotrosLenguajes

función,quesonvoidparaelretornoyunarreglovacíopararepresentarningúnargumento.Deallísimplementellamamosalafunciónprocesar()eimprimimoselresultado.

Enmysistema,esteejemplotomaunosrápidos0.092segundos.

ConclusionComopuedesver,lasbasesdehacerFFIsonmuyfáciles.Porsupuestohaymuchomasquepodríamoshaceraquí.EchaunvistazoalcapituloFFIparamasdetalles.

ElLenguajedeProgramacionRust

64RustdentrodeotrosLenguajes

%RustEfectivo

Entonces,hazaprendidocomoescribiralgodecódigoRust.PerohayunadiferenciaentreescribircualquiercódigoRustyescribirbuencódigoRust.

EstasecciónconsistedetutorialesrelativamenteindependientesquetemuestrancomollevartuRustalsiguientenivel.Patronescomunesycaracterísticasdelabibliotecaestándarseránpresentados.Puedesleerdichasseccionesenelordenqueprefieras.

ElLenguajedeProgramacionRust

65RustEfectivo

%LaPilayelMontículo

Comounlenguajedesistemas,Rustoperaaunbajonivel.Siprovienesdeunlenguajedealtonivel,hayalgunosaspectosdeloslenguajesdeprogramacióndesistemasconloscualespuedasnoestarfamiliarizado.Elmasimportanteeselfuncionamientodelamemoria,conlapilayelmontículo.SiestasfamiliarizadoconelcomolenguajescomoCusanasignacióndesdelapila,estecapituloseraunrepaso.Sinoloestas,aprenderásacercadeesteconceptogeneral,peroconunenfoqueRustero.

ManejodememoriaEstosdostérminoshacenreferenciaaelmanejodelamemoria.Lapilayelmontículosonabstraccionesqueayudanadeterminarcuandoasignaryliberarmemoria.

Heaquíunacomparacióndealtonivel:

Lapilaesmuyrápida,yesdedondelamemoriaesasignadapordefectoenRust.Perolaasignacióneslocalaunallamadaafunción,yeslimitadaentamaño.Elmontículoporotrolado,esmaslento,yesasignadoportuprograma.Peroesefectivamentedeuntamañoilimitado,yesglobalmenteaccesible.

LaPilaHablemosacercadeesteprogramaRust:

fnmain(){

letx=42;

}

Esteprogramaposeeunavariable(variablebinding),x.Lamemoriatienequeserasignadadesdealgúnsitio.Rustasignadesdelapilapordefecto,loquesetraduceenquelosvaloresbásicos‘vanalapila’.Pero,quesignificaesto?

Veamos,cuandounafunciónesllamada,algodememoriaesasignadaparasusvariableslocalesyotrainformaciónextra.Dichamemoriaesllamada‘registrodeactivación’(‘stackframe’),paraelpropósitodeestetutorial,ignoraremoslainformaciónextraysoloconsideraremoslasvariableslocalesalasqueestamosasignandomemoria.Asíqueenestecaso,cuandomain()esejecutada,asignamosunenterode32bitsparanuestroregistrodeactivación.Todoestoesmanejadoautomáticamente,comohaspodidover,notuvimosqueescribirningúncódigoRustespecialoalgunaotracosa.

ElLenguajedeProgramacionRust

66LaPilayelMontículo

Cuandolafuncióntermina,suregistrodeactivaciónesliberadoodesasignado.Estoocurredemaneraautomática,notuvimosquehacernadaespecialacá.

Esoestodoparaestesimpleprograma.Loclaveaentenderaquíesquelaasignacióndememoriadesdelapilaesmuy,muyrápida.Debidoaqueconocemosporadelantadotodaslasvariableslocales,podemosobtenertodalamemoriadeunasolavez.Ydebidoaqueladesecharemostodacompleta,podemosdeshacernosdeellamuyrápido,también.

Ladesventajaesquenopodemosmantenervaloresrondandoporallísilosnecesitamosporunperiodomaslargoqueeltiempodevidadeunafunción.Tampocohemoshabladoacercadequesignificaesenombre,‘pila’.Parahacerlonecesitamosunejemploligeramentemascomplejo:

fnfoo(){

lety=5;

letz=100;

}

fnmain(){

letx=42;

foo();

}

Esteprogramatienetresvariablesentotal:dosenfoo(),unaenmain().Aligualqueantes,cuandomain()esllamada,unsoloenteroesasignadoparasuregistrodeactivación.Peroantesquedemostremosqueesloquepasacuandofoo()esllamada,necesitamosvisualizarqueesloqueestapasandoenmemoria.Tusistemaoperativopresentaatuprogramaunavisiónmuysimple:unalistainmensadedirecciones,desde0hastaunnumeromuygrande,querepresentacuantamemoriaRAMposeelamaquina.PorejemplositienesungigabytedeRAM,tusdireccionesirándesde0hasta1,073,741,824,numeroqueprovienede230,elnumerodebytesenungigabyte.

Estamemoriaesunaespeciedearreglogigante:lasdireccionescomienzanenceroyseincrementanhastaelnumerofinal.Entonces,heaquíundiagramadenuestroprimerregistrodeactivación:

Dirección Nombre Valor

0 x 42

Hemoscolocadoaxenladirección0,conelvalor42

Cuandofoo()esllamadaunnuevoregistrodeactivaciónesasignado:

ElLenguajedeProgramacionRust

67LaPilayelMontículo

Dirección Nombre Valor

2 z 100

1 y 5

0 x 42

Debidoaque0fuereservadoparalaprimeraframe,1y2sonusadosparaelregistrodeactivacióndefoo().Lapilacrecehaciaarriba,amedidaquellamamosamasfunciones.

Hayalgunascosasimportantesquedebemosnotaraquí.Losnúmeros0,1y2existensoloparapropósitosilustrativos,ynoposeenningunarelaciónconlosnúmerosqueunacomputadorarealmenteusaría.Enparticular,laseriededireccionesestánseparadasporunnumerodebytes,yesaseparaciónpuedeinclusoexcedereltamañodelvalorqueestasiendoalmacenado.

Despuésquefoo()termina,suregistrodeactivaciónesliberado:

Dirección Nombre Valor

0 x 42

Yluegodespuésdequemain()finaliza,esteultimovalorseva.Fácil!

Esllamadaunapila(‘stack’)debidoaquefuncionacomounapiladeplatos:elprimerplatoquecolocaseselultimoplatoquesacarás.Laspilassonalgunasvecesllamadas‘colasultimoqueentra,primeroquesale’(‘lastin,firstoutqueues’),porestasrazoneselultimovalorquepusisteenlapilaseráelprimeroqueobtendrásdeella.

Probemosunejemplodetresniveles:

ElLenguajedeProgramacionRust

68LaPilayelMontículo

fnbar(){

leti=6;

}

fnfoo(){

leta=5;

letb=100;

letc=1;

bar();

}

fnmain(){

letx=42;

foo();

}

Bien,enprimerainstancia,llamamosamain():

Dirección Nombre Valor

0 x 42

Actoseguido,main()llamaafoo():

Dirección Nombre Valor

3 c 1

2 b 100

1 a 5

0 x 42

Luegofoo()llamaabar():

Dirección Nombre Valor

4 i 6

3 c 1

2 b 100

1 a 5

0 x 42

Uff!Nuestrapilaestacreciendo.

ElLenguajedeProgramacionRust

69LaPilayelMontículo

Despuésquebar()termina,suregistrodeactivaciónesliberado,dejandosoloafoo()ymain():

Dirección Nombre Valor

3 c 1

2 b 100

1 a 5

0 x 42

Despuésfoo()termina,dejandosoloamain()

Dirección Nombre Valor

0 x 42

Hemosterminadoentonces.Seentiende?Escomoapilarplatos:agregasaltopeysacasdeel.

ElMontículoAhora,todoestotrabajabien,peronotodofuncionadeesamanera.Algunasveces,necesitaspasarmemoriaentrediferentesfunciones,omantenermemoriavivaporuntiempomayorquelaejecucióndeunafunción.Paraestousamoselmontículo.

EnRust,puedesasignarmemoriadesdeelmontículoconeltipoBox<T>(caja).

Heaquiunejemplo:

fnmain(){

letx=Box::new(5);

lety=42;

}

Acá,loquesucedecuandomain()esllamada:

Dirección Nombre Valor

1 y 42

0 x ??????

Asignamosespacioparadosvariablesenlapila.yes42,comoconocemoshastaahora,peroqueacercadex?Bueno,xesunBox<i32>,ylascajas(boxes)asignanmemoriadesdeelmontículo.Elvalordelacajaencuestiónesunaestructuraqueposeeun

ElLenguajedeProgramacionRust

70LaPilayelMontículo

apuntadora‘elmontículo’.Cuandocomienzalaejecucióndelafunción,yBox::new()esllamada,estaasignaalgodememoriaparaelmontículoycoloca5allí.Lamemoriaahoraluceasí:

Dirección Nombre Valor

230 5

... ... ...

1 y 42

0 x 230

Tenemos230ennuestracomputadorahipotéticacon1GBdeRAM.Ydebidoaquenuestrapilacrecedesdecero,laformamasfácilparaasignarmemoriaesdesdeelotroextremo.Entoncesnuestroprimervalorestaenellugarmasaltoenlamemoria.Yelvalordelaestructuraenxtieneunapuntadorplano(rawpointer)aellugarquehemosasignadoenelmontículo,entonceselvalordexes230,ladireccióndelamemoriaquehemossolicitado.

Nohemoshabladomuchoacercadequesignificaenrealidadasignaryliberarmemoriaenestoscontextos.Entrarenelprofundodetalledeelloestafueradelalcancedeestetutorial,loimportantearesaltaresqueelmontículonoesunasimplepilaquecrecedesdeelladoopuesto.Tendremosunejemplodeestomasadelanteenellibro,perodebidoaqueelmontículopuedeserasignadoyliberadoencualquierorden,puedeterminarcon‘vacios’.Heaquíundiagramadeladistribucióndelamemoriadeunprogramaquehaestadocorriendoporalgúntiempo:

Dirección Nombre Valor

230 5

(230)-1

(230)-2

(230)-3 42

... ... ...

3 y (230)-3

2 y 42

1 y 42

0 x 230

ElLenguajedeProgramacionRust

71LaPilayelMontículo

Enestecaso,hemosasignadocuatrocosasenelmontículo,perohemosliberadodosdeellas.Hayunvacíoentre230y(230)-3quenoestasiendousadoactualmente.Eldetalleespecificoacercadecomoyporqueestosucededependedelaestrategiausadaparamanejarelmontículo.Diferentesprogramaspuedenusardiferentes‘asignadoresdememoria’(‘memoryallocators’),quesonbibliotecasencargadasdemanejarlaasignacióndememoriaporti.LosprogramasenRustusanjemallocparadichopropósito.

Decualquiermodo,ydevueltaanuestroejemplo.Debidoaqueestamemoriaestaenelmontículo,puedepermanecervivamastiempoquelafunciónquecrealacaja(box).Sinembargo,enestecaso,estonosucede.movingcuandolafuncióntermina,necesitamosliberarelregistrodeactivacióndemain().Box<T>,sinembargo,tienenuntrucobajolamanga:Drop.LaimplementacióndeDropparaBoxliberalamemoriaquehasidoasignadacuandolacajaescreada.Grandioso!Asíquecuandoxseva(saledecontexto),primeroliberalamemoriaasignadadesdeelmontículo:

Dirección Nombre Valor

1 y 42

0 x ??????

moving.Podemoshacerquelamemoriapermanezcavivamastiempotransfiriendolapertenencia(ownership),algunasvecesllamado‘moviendofueradelacaja’(‘movingoutofthebox’).Ejemplosmascomplejosseráncubiertosmasadelante.↩

Luegoelregistrodeactivaciónseva,liberandotodanuestramemoria.

Argumentosyprestamo(borrowing)Hemosllevadoacaboalgunosejemplosbásicosconlapilayelmontículo,peroquehayacercadelosargumentosafuncionesyelpréstamo(borrowing)?HeaquíunpequeñoprogramaRust:

fnfoo(i:&i32){

letz=42;

}

fnmain(){

letx=5;

lety=&x;

foo(y);

}

ElLenguajedeProgramacionRust

72LaPilayelMontículo

Cuandoentramosamain(),lamemorialucedelasiguientemanera:

Dirección Nombre Valor

1 y 0

0 x 5

xesunsimple5,yyesunareferenciaax.Entonces,elvalordeyesladireccióndememoriaenlaquexvive,queenestecasoes0.

Quesucedecuandollamamosafoo()pasandoaycomoargumento?

Dirección Nombre Valor

3 z 42

2 i 0

1 y 0

0 x 5

Losregistrosdeactivaciónnosonsoloparavariableslocales,sontambiénparaargumentos.Enestecaso,necesitamostenerambosi,nuestroargumento,yznuestravariablelocal.iesunacopiadelargumento,y.Debidoaqueelvalordeyes0entonceseseeselvalordei.

Estaesunarazónporlacualtomarprestadaunavariablenoliberaningunamemoria:elvalordelareferenciaessolounapuntadoraunadireccióndememoria.Sinosdeshiciéramosdelamemoriasubyacente,lascosasnoiríandeltodobien.

UnejemplocomplejoBien,vayamosatravésdeesteprogramacomplejopaso-a-paso:

ElLenguajedeProgramacionRust

73LaPilayelMontículo

fnfoo(x:&i32){

lety=10;

letz=&y;

baz(z);

bar(x,z);

}

fnbar(a:&i32,b:&i32){

letc=5;

letd=Box::new(5);

lete=&d;

baz(e);

}

fnbaz(f:&i32){

letg=100;

}

fnmain(){

leth=3;

leti=Box::new(20);

letj=&h;

foo(j);

}

Primero,llamamosamain():

Dirección Nombre Valor

230 20

... ... ...

2 j 0

1 i 230

0 h 3

Asignamosmemoriaparaj,i,yh.iestaenelmontículo,esporelloquesuvalorapuntahaciael.

Acontinuation,alfinaldemain(),foo()esllamada:

ElLenguajedeProgramacionRust

74LaPilayelMontículo

Dirección Nombre Valor

230 20

... ... ...

5 z 4

4 y 10

3 x 0

2 j 0

1 i 230

0 h 3

Esasignadoespacioparax,y,yz.Elargumentoxtieneelmismovalorquej,debidoaqueesofueloqueleproporcionamosalafunción.Esunapuntadoraladirección0,puestoquejapuntaah.

Seguidamente,foo()llamaabaz(),pasándolez:

Dirección Nombre Valor

230 20

... ... ...

7 g 100

6 f 4

5 z 4

4 y 10

3 x 0

2 j 0

1 i 230

0 h 3

Hemosasignadomemoriaparafyg.baz()esmuycorta,asíquecuandotermina,nosdeshacemosdesuregistrodeactivación:

ElLenguajedeProgramacionRust

75LaPilayelMontículo

Dirección Nombre Valor

230 20

... ... ...

5 z 4

4 y 10

3 x 0

2 j 0

1 i 230

0 h 3

Después,foo()llamaabar()conxyz:

Dirección Nombre Valor

230 20

(230)-1 5

... ... ...

10 e 9

9 d (230)-1

8 c 5

7 b 4

6 a 0

5 z 4

4 y 10

3 x 0

2 j 0

1 i 230

0 h 3

Terminamosasignandootrovalorenelmontículo,asíquetenemosquerestarunoa230.Esmasfácilescribiresoque1,073,741,823.Encualquiercaso,seteamoslasvariablescomoyaesusual.

Alfinaldebar(),estallamaabaz():

ElLenguajedeProgramacionRust

76LaPilayelMontículo

Dirección Nombre Valor

230 20

(230)-1 5

... ... ...

12 g 100

11 f 9

10 e 9

9 d (230)-1

8 c 5

7 b 4

6 a 0

5 z 4

4 y 10

3 x 0

2 j 0

1 i 230

0 h 3

Conesto,estamosennuestropuntomasprofundo!Wow!Felicitacionesporhaberseguidotodoestoyhaberllegadotanlejos.

Luegobaz()termina,nosdeshacemosdefyg:

ElLenguajedeProgramacionRust

77LaPilayelMontículo

Dirección Nombre Valor

230 20

(230)-1 5

... ... ...

10 e 9

9 d (230)-1

8 c 5

7 b 4

6 a 0

5 z 4

4 y 10

3 x 0

2 j 0

1 i 230

0 h 3

Acontinuación,retornamosdebar().denestecasoesunBox<T>,entoncestambiénliberaaloqueapunta:(230)-1.

Dirección Nombre Valor

230 20

... ... ...

5 z 4

4 y 10

3 x 0

2 j 0

1 i 230

0 h 3

Después,foo()retorna:

ElLenguajedeProgramacionRust

78LaPilayelMontículo

Dirección Nombre Valor

230 20

... ... ...

2 j 0

1 i 230

0 h 3

Entonces,finalmentemain()retorna,locuallimpiaelresto.Cuandoiesliberada(atravésdeDrop)estalimpiaratambiénlorestanteenelmontículo.

Quehacenotroslenguajes?Lamayoríadeloslenguajesconunrecolectordebasuraasignandesdeelmontículopordefecto.Estosignificaquetodoslosvaloresestándentrodecajas(boxed).Existenunnumeroderazonesporlacualesestosehacedeestamanera,peroestánfueradelalcancedeestetutorial.También,existenalgunasoptimizacionesquehacenqueestonosea100%verdadtodoeltiempo.EnvezdeconfiarenlapilayDropparalimpiarlamemoria,elrecolectordebasuraeselencargadodeadministrarelmontículo.

Cualusar?Silapilaesmasrápidaymasfácildeusar,porquenecesitamoselmontículo?UnagranrazónesquelaasignacióndesdelapilasignificaquesolotienessemánticaLIFOparareclamaralmacenamiento.Laasignacióndesdeelmontículoesestrictamentemasgeneral,permitiendoqueelalmacenamientopuedasertomadoyretornadoaelpoolenordenarbitrario,peroconuncostoencomplejidad.

Generalmente,deberíaspreferirasignacióndesdelapila,esporelloqueRustasignadesdelapilapordefecto.ElmodeloLIFOdelapilaesmassimple,anivelfundamental.Estotienedosgrandesimpactos:eficienciaentiempodeejecucióneimpactosemántico.

EficienciaentiempodeEjecucion.Administrarlamemoriaparalapilaestrivial:Lamaquinasimplementeincrementaunsolovalor,elllamado"apuntadoralapila"(“stackpointer”).Laadministracióndememoriaparaelmontículonoloes:Lamemoriaasignadadesdeelmontículoesliberadaenpuntos

ElLenguajedeProgramacionRust

79LaPilayelMontículo

arbitrarios,ycadabloquedememoriaasignadadesdeelmontículopudeserdeuntamañoarbitrario,eladministradordememoriageneralmentedebetrabajarmuchomasduroparaidentificarmemoriaquepuedaserreusada.

Siquisierassumergirtemasenestetópicoconmayordetalle,estepaperesunamuybuenaintroducción.

ImpactosemanticoStack-allocationimpactstheRustlanguageitself,andthusthedeveloper’smentalmodel.TheLIFOsemanticsiswhatdriveshowtheRustlanguagehandlesautomaticmemorymanagement.Eventhedeallocationofauniquely-ownedheap-allocatedboxcanbedrivenbythestack-basedLIFOsemantics,asdiscussedthroughoutthischapter.Theflexibility(i.e.expressiveness)ofnonLIFO-semanticsmeansthatingeneralthecompilercannotautomaticallyinferatcompile-timewherememoryshouldbefreed;ithastorelyondynamicprotocols,potentiallyfromoutsidethelanguageitself,todrivedeallocation(referencecounting,asusedbyRcandArc,isoneexampleofthis).

LaasignacióndesdelapilaimpactaaRustcomolenguaje,yconelloelmodelomentaldeldesarrollador.LasemánticaLIFOesloqueconducecomoellenguajeRustmanejaelmanejoautomáticodememoria.InclusolaliberacióndeunacajaasignadadesdeelmontículoconunúnicodueñopuedesermanejadaporlasemánticaLIFO,talycomosehadiscutidoenestecapitulo.Laflexibilidad(e.j.expresividad)delasemánticano-LIFOsignificaqueengeneralelcompiladornopuedeinferirdemaneraautomáticayentiempodecompilacióndondelamemoriadeberíaserliberada;tienequeapoyarseenprotocolosdinámicos,potencialmenteexternosaellenguaje,paraefectuarliberacióndememoria(conteodereferencias,comoelusadoenRc<T>yArc<T>,esunejemplo).

Cuandosellevaalextremo,elmayorpoderexpresivodelaasignacióndesdeelmontículovieneacostodebienseasoportesignificativoentiempodeejecución(e.j.enlaformadeunrecolectordebasura)oesfuerzosignificativoporpartedelprogramador(enlaformadellamadasmanualesexplícitasquerequierenverificaciónnoproporcionadaporelcompiladordeRust).

ElLenguajedeProgramacionRust

80LaPilayelMontículo

%Pruebas

Probarprogramaspuedeserunaformaefectivademostrarlapresenciadebugs,peroesdesesperanzadamenteinadecuadaparamostrarsuausencia.EdsgerW.Dijkstra,"TheHumbleProgrammer"(1972)

HablaremosacercadecomoprobarcódigoRust.DeloquenoestaremoshablandoesacercadelamaneracorrectadeprobarcódigoRust.Haymuchasescuelasdepensamientoenrelaciónalaformacorrectaeincorrectadeescribirpruebas.Todosesosenfoquesusanlasmismasherramientasbásicas,enestaseccióntemostraremoslasintaxisparahacerusodeellas.

Elatributo testEnesencia,unapruebaenRustesunafunciónqueestaanotadaconelatributotest.VamosacrearunnuevoproyectollamadosumadorconCargo:

$cargonewsumador

$cdadder

Cargogeneraraautomáticamenteunapruebasimplecuandocreasunproyectonuevo.Heaquielcontenidodesrc/lib.rs:

#[test]

fnit_works(){

}

Notael#[test].Esteatributoindicaqueestaesunafuncióndeprueba.Actualmentenotienecuerpo.Peroesoessuficienteparapasar!Podemosejecutarlostestsconcargotest:

ElLenguajedeProgramacionRust

81Pruebas

$cargotest

Compilingsumadorv0.1.0(file:///Users/goyox86/Code/rust/sumador)

Runningtarget/debug/sumador-ba17f4f6708ca3b9

running1test

testit_works...ok

testresult:ok.1passed;0failed;0ignored;0measured

Doc-testssumador

running0tests

testresult:ok.0passed;0failed;0ignored;0measured

Cargocompiloyejecutonuestrostests.Haydosconjuntosdesalidaaquí:unoparalaspruebasquenosotrosescribimos,yotroparalostestsdedocumentación.Hablaremosacercadeestosmastarde.Porahoraveamosestalinea:

testit_works...ok

Notaelit_works.Provienedelnombredenuestrafunción:

fnit_works(){

#}

Tambiénobtenemosunalineaderesumen:

testresult:ok.1passed;0failed;0ignored;0measured

Entonces,porquenuestraspruebasvacíaspasan?Cualquierpruebaquenohagapanic!pasa,ycualquierpruebaquehacepanic!falla.Hagamosfallaranuestraprueba:

#[test]

fnit_works(){

assert!(false);

}

assert!esunamacroproporcionadaporRustlacualtomaunargumento:sielargumentoestrue,nadapasa.Sielargumentoesfalse,assert!hacepanic!.Ejecutemosnuestraspruebasotravez:

ElLenguajedeProgramacionRust

82Pruebas

$cargotest

Compilingsumadorv0.1.0(file:///Users/goyox86/Code/rust/sumador)

Runningtarget/debug/sumador-ba17f4f6708ca3b9

running1test

testit_works...FAILED

failures:

----it_worksstdout----

thread'it_works'panickedat'assertionfailed:false',src/lib.rs:3

failures:

it_works

testresult:FAILED.0passed;1failed;0ignored;0measured

thread'<main>'panickedat'Sometestsfailed',/Users/rustbuild/src/rust-buildbot/slave/stable-dist-rustc-mac/build/src/libtest/lib.rs:

Rustnosindicaquenuestrapruebahafallado:

testit_works...FAILED

Ysereflejaenlalineaderesumen:

testresult:FAILED.0passed;1failed;0ignored;0measured

Tambiénobtenemosunvalorderetornodiferenteacero:

$echo$?

101

Estoesmuyútilparaintegrarcargotestconotrasherramientas.

Podemosinvertirlafalladenuestraspruebasconotroatributo:should_panic:

#[test]

#[should_panic]

fnit_works(){

assert!(false);

}

Estaspruebastendránéxitosihacemospanic!yfallaransisecompletan.Probemos:

ElLenguajedeProgramacionRust

83Pruebas

$cargotest

Compilingsumadorv0.1.0(file:///Users/goyox86/Code/rust/sumador)

Runningtarget/debug/sumador-ba17f4f6708ca3b9

running1test

testit_works...ok

testresult:ok.1passed;0failed;0ignored;0measured

Doc-testssumador

running0tests

testresult:ok.0passed;0failed;0ignored;0measured

Rustproporcionaotramacro,assert_eq!,quecomparadosargumentosparaverificarigualdad:

#[test]

#[should_panic]

fnit_works(){

assert_eq!("Hola","mundo");

}

Estapruebapasaofalla?Debidoalapresenciadelatributoshould_panic,pasa:

$cargotest

Compilingsumadorv0.1.0(file:///Users/goyox86/Code/rust/sumador)

Runningtarget/debug/sumador-ba17f4f6708ca3b9

running1test

testit_works...ok

testresult:ok.1passed;0failed;0ignored;0measured

Doc-testssumador

running0tests

testresult:ok.0passed;0failed;0ignored;0measured

Laspruebasshould_panicpuedenserfrágiles,esdifícilgarantizarquelapruebanofallóporunarazóninesperada.Paraayudarenesto,unparámetroopcionalexpectedpuedeseragregadoaelatributoshould_panic.Lapruebaseaseguraraqueelmensajedeerrorcontengaelmensajeproporcionado.Unaversiónmasseguradelapruebaseria:

ElLenguajedeProgramacionRust

84Pruebas

#[test]

#[should_panic(expected="assertionfailed")]

fnit_works(){

assert_eq!("Hola","mundo");

}

Esofuetodoparalobásico!Escribamosunaprueba'real':

pubfnsuma_dos(a:i32)->i32{

a+2

}

#[test]

fnit_works(){

assert_eq!(4,suma_dos(2));

}

Esteesunusomuycomúndeassert_eq!:llamaralgunafunciónconalgunosargumentosconocidosycompararlasalidadedichallamadaconlasalidaesperada.

Elmodulo testsHayunaformaenlacualnuestroejemplonoesidiomático:lefaltaelmodulotests.Lamaneraidiomáticadeescribirnuestroejemploluceasí:

pubfnsuma_dos(a:i32)->i32{

a+2

}

#[cfg(test)]

modtests{

usesuper::suma_dos;

#[test]

fnit_works(){

assert_eq!(4,suma_dos(2));

}

}

Hayunoscuantoscambiosacá.Elprimeroeslainclusiondeunmodtestsconunatributocfg.Elmodulonospermiteagrupartodasnuestraspruebas,ytambiénnospermitedefinirfuncionesdesoportedesernecesario,todoesonoformapartedenuestrocrate.Elatributo

ElLenguajedeProgramacionRust

85Pruebas

cfgsolocompilanuestrocódigodepruebassiestuviéramosintentandocorrerlaspruebas.Estopuedeahorrartiempodecompilación,tambiénseaseguraquenuestraspruebasquedencompletamenteexcluidasdeunacompilaciónnormal.

Elsegundocambioesladeclaraciónuse.Debidoaqueestamosenunmodulointerno,necesitamoshacedisponibleanuestrapruebadentrodenuestroámbitoactual.Estopuedesermolestosiposeesunmodulogrande,yesporelloqueescomúnelusodelafacilidadglob.Cambiemosnuestrosrc/lib.rsparaquehagausodeello:

pubfnsuma_dos(a:i32)->i32{

a+2

}

#[cfg(test)]

modtests{

usesuper::*;

#[test]

fnit_works(){

assert_eq!(4,suma_dos(2));

}

}

Notalalineausediferente.Ahoraejecutamosnuestraspruebas:

$cargotest

Compilingsumadorv0.1.0(file:///Users/goyox86/Code/rust/sumador)

Runningtarget/debug/sumador-ba17f4f6708ca3b9

running1test

testtests::it_works...ok

testresult:ok.1passed;0failed;0ignored;0measured

Doc-testssumador

running0tests

testresult:ok.0passed;0failed;0ignored;0measured

Funciona!

Laconvenciónactualesusarelmodulotestsparacontenertuspruebasde"estilo-unitario".Cualquiercosaquesolopruebeunpequeñopedazodefuncionalidadvaaquí.Peroqueacercadelaspruebas"estilo-integracion"?Paraellas,tenemoseldirectoriotests.

ElLenguajedeProgramacionRust

86Pruebas

Eldirectorio testsParaescribirunapruebadeintegración,creemosundirectoriotestsycoloquemosunarchivotests/lib.rsdentro,conelsiguientedecontenido:

externcratesumador;

#[test]

fnit_works(){

assert_eq!(4,sumador::suma_dos(2));

}

Lucesimilaranuestraspruebasanteriores,peroligeramentediferente.Ahoratenemosunexterncratesumadoralprincipio.Estoesdebidoaquelaspruebaseneldirectoriosonuncrateseparado,entoncesdebemosimportarnuestrabiblioteca.Estoestambiénelporquetestsesunlugaridóneoperaescribirtestsdeintegración:estaspruebasusanlabibliotecajustocomocualquierotroconsumidorloharía.

Ejecutemoslas:

$cargotest

Compilingsumadorv0.1.0(file:///Users/goyox86/Code/rust/sumador)

Runningtarget/debug/lib-f71036151ee98b04

running1test

testit_works...ok

testresult:ok.1passed;0failed;0ignored;0measured

Runningtarget/debug/sumador-ba17f4f6708ca3b9

running1test

testtests::it_works...ok

testresult:ok.1passed;0failed;0ignored;0measured

Doc-testssumador

running0tests

testresult:ok.0passed;0failed;0ignored;0measured

Ahoratenemostressecciones:nuestraspruebasanteriorestambiénfueronejecutadas,juntoconlanuevapruebadeintegración.

ElLenguajedeProgramacionRust

87Pruebas

Esofuetodoparaeldirectoriotests.Elmodulotestsnoesnecesarioaquí,debidoaqueelmodulocompletoestadedicadoapruebas.

Finalmenteechemosunvistazoaesatercerasección:pruebasdedocumentación.

PruebasdedocumentaciónNadaesmejorquedocumentaciónconejemplos.Nadaespeorqueejemplosquenofuncionan,debidoaqueelcódigoacambiadodesdequeladocumentaciónfueescrita.Respectoaesto,Rustsoportalaejecuciónautomáticadelosejemplospresentesentudocumentación.Heaquíunsrc/lib.rspulidoconejemplos:

//!The`adder`crateprovidesfunctionsthataddnumberstoothernumbers.

//!

//!#Examples

//!

//!

//!assert_eq!(4,adder::add_two(2));//!```

///Thisfunctionaddstwotoitsargument.//////#Examples/////////useadder::add_two;//////assert_eq!(4,add_two(2));///pubfnadd_two(a:i32)->i32{a+2}

[cfg(test)]modtests{usesuper::*;

#[test]

fnit_works(){

assert_eq!(4,add_two(2));

}

}

ElLenguajedeProgramacionRust

88Pruebas

Notaladocumentaciónaniveldemodulocon`//!`yladocumentaciónaniveldefuncióncon`///`.LadocumentacióndeRustsoportaMarkdownencomentariosygraves(\`\`\`)triplesdelimitanbloquesdecódigo.Esconvencionalincluirlasección`#Examples`,exactamenteasi,seguidaporlosejemplos.

Ejecutemoslaspruebasnuevamente:

```bash

$cargotest

Compilingsumadorv0.1.0(file:///Users/goyox86/Code/rust/sumador)

Runningtarget/debug/lib-f71036151ee98b04

running1test

testit_works...ok

testresult:ok.1passed;0failed;0ignored;0measured

Runningtarget/debug/sumador-ba17f4f6708ca3b9

running1test

testtests::it_works...ok

testresult:ok.1passed;0failed;0ignored;0measured

Doc-testssumador

running2tests

test_0...ok

testsuma_dos_0...ok

testresult:ok.2passed;0failed;0ignored;0measured

Ahoratenemoslostrestiposdepruebascorriendo!Notalosnombresdelaspruebasdedocumentación:el_0esgeneradoparalapruebadelmodulo,ysuma_dos_0paralapruebadefunción.Estosnúmerosseautoincrementaranconnombrescomosuma_dos_1amedidaquemasejemplossonagregados.

ElLenguajedeProgramacionRust

89Pruebas

%CompilaciónCondicional

Rustposeeunatributoespecial,#[cfg],quetepermitecompilarcódigobasadoenunaopciónproporcionadaalcompilador.Tienedosformas:

#[cfg(foo)]

#fnfoo(){}

#[cfg(bar="baz")]

#fnbar(){}

Tambiénposeealgunoshelpers:

#[cfg(any(unix,windows))]

#fnfoo(){}

#[cfg(all(unix,target_pointer_width="32"))]

#fnbar(){}

#[cfg(not(foo))]

#fnnot_foo(){}

Loscualespuedenseranidadosdemaneraarbitraria:

#[cfg(any(not(unix),all(target_os="macos",target_arch="powerpc")))]

#fnfoo(){}

Paraactivarodesactivarestosswitches,siestasusandoCargo,seconfiguranenlasección[features]detuCargo.toml

[features]

#Nofeaturespordefecto

default=[]

#Elfeature“secure-password”dependeenelpaquetebcrypt.

secure-password=["bcrypt"]

Cuandohacemosesto,Cargopasaunaopciónarustc:

--cfgfeature="${feature_name}"

Lasumadeesasopcionescfgdeterminaracualessonactivadas,yenconsecuencia,cualcódigoseracompilado.Tomemosestecódigo:

ElLenguajedeProgramacionRust

90CompilaciónCondicional

#[cfg(feature="foo")]

modfoo{

}

Silocompilamosconcargobuild--features"foo",Cargoenviaralaopción--cfgfeature="foo"arustc,ylasalidatendráraelmodfoo.Sicompilamosconuncargobuildnormal,ningunaopciónextraseraproporcionada,ydebidoaesto,ningúnmodulofooexistirá.

cfg_attrTambiénpuedesconfigurarotroatributobasadoenunavariablecfgconcfg_attr:

#[cfg_attr(a,b)]

#fnfoo(){}

Seralomismoque#[b]siaesconfiguradoporunatributocfg,ynadadecualquierotramanera.

cfg!Laextensiondesintaxistepermitetambiénusarestetipodeopcionesencualquierotropuntodetucódigo:

ifcfg!(target_os="macos")||cfg!(target_os="ios"){

println!("ThinkDifferent!");

}

Estosseránreemplazadoportrueofalseentiempodecompilación,dependiendoenlasopcionesdelaconfiguración.

ElLenguajedeProgramacionRust

91CompilaciónCondicional

%Documentación

LadocumentaciónesunaparteimportantedecualquierproyectodesoftwareyunciudadanodeprimeraclaseenRust.HablemosacercadelasherramientasqueRustteproporcionaparadocumentartusproyectos.

AcercaderustdocLadistribucióndeRustincluyeunaherramienta,rustdoc,encargadadegenerarladocumentación.rustdocestambiénusadaporCargoatravésdecargodoc.

Ladocumentaciónpuedesergeneradadedosformas:desdeelcódigofuente,odesdearchivosMarkdown.

DocumentandocódigofuenteLaprincipalformadedocumentarunproyectoRustesatravésdelaanotacióndelcódigofuente.Paraestepropósito,puedesusarcomentariosdedocumentación:

///Construyeunnuevo`Rc`.

///

///#Examples

///

///

///usestd::rc::Rc;//////letcinco=Rc::new(5);///```pubfnnew(value:T)->Rc{//laimplementaciónvaaqui}

Elcódigoanteriorgeneradocumentaciónquelucecomo[esta][rc-new](ingles).Hedejadolaimplementaciónporfuera,conuncomentarioregularensulugar.Esaeslaprimeracosaaresaltaracercadeestaanotación:usa`///`,envezde`//`.Elslashtripleindicaqueesuncomentariodedocumentación.

LoscomentariosdedocumentaciónestánescritosenformatoMarkdown.

Rustmantieneunregistrodedichoscomentarios,registroqueusaalmomentodegenerarladocumentación.Estoesimportantecuandosedocumentancosascomoenumeraciones(enums):

```rust

///Eltipo`Option`.Vea[ladocumentaciónaniveldemodulo](../)paramasinformación.

enumOption<T>{

///Ningúnvalor

None

///Algúnvalor`T`

Some(T),

}

ElLenguajedeProgramacionRust

92Documentación

Loanteriorfunciona,peroesto,no:

///Eltipo`Option`.Vea[ladocumentaciónaniveldemodulo](../)paramasinformación.

enumOption{

None,///Ningúnvalor

Some(T),///Algúnvalor`T`

}

Obtendrásunerror:

hola.rs:4:1:4:2error:expectedident,found`}`

hola.rs:4}

^

Estedesafortunadoerrorescorrecto:loscomentariosdedocumentaciónaplicansoloaloqueestedespuésdeellos,ynohaynadadespuésdelultimocomentario.

Escribiendocomentariosdedocumentación

Decualquiermodo,cubramoscadapartedeestecomentarioendetalle:

///Construyeunnuevo`Rc<T>`.

#fnfoo(){}

Laprimeralineadeuncomentariodedocumentacióndebeserunresumencortodesusfuncionalidad.Unaoración.Sololobásico.Dealtonivel.

///

///Otrosdetallesacercadelaconstrucciónde`Rc<T>`s,quizásdescribiendosemántica

///complicada,talvezopcionesadicionales,cualquiercosaextra.

///

#fnfoo(){}

Nuestroejemplooriginalsoloteniaunalineaderesumen,perosihubiésemostenidomascosasquedecir,pudimoshaberagregadomasexplicaciónenunpárrafonuevo.

Seccionesespeciales

///#Examples

#fnfoo(){}

ElLenguajedeProgramacionRust

93Documentación

Acontinuaciónestánlasseccionesespeciales.Estassonindicadasconunacabecera,#.Haytrestiposdecabeceraqueseusancomúnmente.Estosnosonsintaxisespecial,soloconvención,porahora.

///#Panics

#fnfoo(){}

Maloseirrecuperablesusosdeunafunción(e.j.Erroresdeprogramación)enRustsonusualmenteindicadosporpánicos(panics),loscualesmatanelhiloactualcomomínimo.Situfunciónposeeuncontratonotrivialcomoeste,queesdetectado/impuestoporpánicos,documentarloesmuyimportante.

///#Failures

#fnfoo(){}

SitufunciónométodoretornaunResult<T,E>,entoncesdescribirlascondicionesbajolascualesretornaErr(E)esalgobuenoporhacer.EstoesligeramentemenosimportantequePanics,aconsecuenciadequeescodificadoenelsistemadetipos,peroesaun,algoqueserecomiendahacer.

///#Safety

#fnfoo(){}

Situfunciónesunsafe(insegura),deberíasexplicarcualessonlasinvariantesquedebensermantenidasporelllamador.

///#Examples

///

///

///usestd::rc::Rc;//////letcinco=Rc::new(5);///```

fnfoo(){}

ElLenguajedeProgramacionRust

94Documentación

Tercero,`Examples`,incluyeunoomasejemplosdelusodetufunciónométodo,ytususuariostequerrán.Estosejemplosvandentrodeanotacionesdebloquesdecódigo,deloscualeshablaremosenunmomento,puedentenermasdeunasección:

```rust

///#Examples

///

///Patrones`&str`simples:

///

///

///letv:Vec<&str>="Maryteniauncorderito".split('').collect();///assert_eq!(v,vec!["Mary","tenia","un","corderito"]);/////////Patronesmascomplejosconlambdas://///////letv:Vec<&str>="abc1def2ghi".split(|c:char|c.is_numeric()).collect();///assert_eq!(v,vec!["abc","def","ghi"]);///```

fnfoo(){}

Discutamoslosdetallesdeesosbloquesdecódigo.

####Anotacionesdebloquesdecódigo

ParaescribiralgunacódigoRustenuncomentario,usalosgravestriples:

```rust

///

///println!("Hola,mundo");///```

fnfoo(){}

SiquierescódigoquenoseaRust,puedesagregarunaanotación:

```rust

///```c

///printf("Hola,mundo\n");

///

ElLenguajedeProgramacionRust

95Documentación

fnfoo(){}

Lasintaxisdeestasecciónseraresaltadadeacuerdoallenguajequeestésmostrando.Sisoloestasmostrandotextoplano,usa`text`.

Acá,esimportanteelegirlaanotacióncorrecta,debidoaque`rustdoc`lausadeunamanerainteresante:Puedeserusadaparaprobartusejemplos,detalmaneraquenosevuelvanobsoletosconeltiempo.SitienesalgúncódigoCpero`rustdoc`piensaqueesRust,esporqueolvidastelaanotación,`rustdoc`sequejaraalmomentodetratardegenerarladocumentación.

##Documentacióncomopruebas

Discutamosnuestradocumentacióndeejemplo:

```rust

///

///println!("Hola,mundo");///```

fnfoo(){}

Notarasquenonecesitasuna`fnmain()`oalgomas.`rustdoc`agregaraunmain()automáticamentealrededordetucódigo,yenellugarcorrecto.Porejemplo:

```rust

///

///usestd::rc::Rc;//////letcinco=Rc::new(5);///```

fnfoo(){}

Seconvertiráenlaprueba:

```rust

fnmain(){

usestd::rc::Rc;

letcinco=Rc::new(5);

}

Heaquíelalgoritmocompletoquerustdocusaparapost-procesarlosejemplos:

1. Cualquieratributo#![foo]sobranteesdejadointactocomoatributodelcrate.

ElLenguajedeProgramacionRust

96Documentación

2. Algunosatributoscomunessoninsertados,incluyendounused_variables,unused_assignments,unused_mut,unused_attributes,ydead_code.Ejemplospequeñosocasionalmentedisparanestoslints.

3. Sielejemplonocontieneexterncrate,entonceselexterncrate<micrate>;esinsertado.

4. Finalmente,sielejemplonocontienefnmain,eltextoesenvueltoenfnmain(){tu_codigo}

Algunasveces,todoestonoessuficiente.Porejemplo,todosestosejemplosdecódigocon///delosquehemosestadohablando?Eltextoplano:

///Algodedocumentación.

#fnfoo(){}

Lucediferentealasalida:

///Algodedocumentación.

#fnfoo(){}

Si,escorrecto:puedesagregarlineasquecomiencencon#,yestasseráneliminadasdelasalida,peroseránusadasenlacompilacióndetucódigo.Puedesusarestocomoventaja.Enestecaso,loscomentariosdedocumentaciónnecesitanaplicaraalgúntipodefunción,entoncessiquieromostrarsolouncomentariodedocumentación,necesitoagregarunapequeñadefinicióndefuncióndebajo.Almismotiempo,estaallísoloparasatisfaceralcompilador,demaneratalqueesconderlahaceelejemplomaslimpio.Puedesusarestatécnicaparaexplicarejemplosmaslargosendetalle,preservandoaunlacapacidaddetudocumentaciónparaserprobada.Porejemplo,estecódigo:

letx=5;

lety=6;

println!("{}",x+y);

Heaquíunaexplicación,renderizada:

Primero,asignamosaxelvalordecinco:

letx=5;

#lety=6;

#println!("{}",x+y);

Acontinuación,asignamosseisay:

ElLenguajedeProgramacionRust

97Documentación

#letx=5;

lety=6;

#println!("{}",x+y);

Finalmente,imprimimoslasumadexyy:

#letx=5;

#lety=6;

println!("{}",x+y);

Heaquílamismaexplicación,entextoplano:

Primero,asignamosaxelvalordecinco:

letx=5;

#lety=6;

#println!("{}",x+y);

Acontinuación,asignamosseisay:

#letx=5;

lety=6;

#println!("{}",x+y);

Finalmente,imprimimoslasumadexyy:

#letx=5;

#lety=6;

println!("{}",x+y);

Alrepetirtodaslaspartesdelejemplo,puedesasegurartequetuejemploauncompila,mostrandosololaspartesrelevantesatuexplicación.

Documentandomacros

Heaquíunejemplodeladocumentaciónaunamacro:

///Panicconunmensajeproporcionadoamenosquelaexpressionseaevaluadaatrue.

///

///#Examples

///

///

ElLenguajedeProgramacionRust

98Documentación

///##[macro_use]externcratefoo;///#fnmain(){///panic_unless!(1+1==2,“Lasmathematicasestanrotas.”);///#}/////////should_panic///##[macro_use]externcratefoo;///#fnmain(){///panic_unless!(true==false,“Yoestoyroto.”);///#}///```

[macro_export]macro_rules!panic_unless{($condition:expr,$($rest:expr),+)=>({if!$condition{panic!($($rest),+);}});}

fnmain(){}

Notarastrescosas:necesitamosagregarnuestropropialinea`externcrate`,detalmaneraquepodamosagregarelatributo`#[macro_use]`.Segundo,necesitaremosagregarnuestrapropia`main()`.Finalmente,unusojuiciosode`#`paracomentaresasdoscosas,demaneraquenossemuestrenenlasalida.

###Ejecutandopruebasdedocumentación

Paracorrerlaspruebaspuedes:

```bash

$rustdoc--testruta/a/mi/crate/root.rs

$cargotest

Correcto,cargotestpruebaladocumentaciónembebidatambién.Sinembargo,cargotest,noprobaracratesbinarios,solobibliotecas.Estodebidoalaformaenlaquerustdocfunciona:enlazaconlabibliotecaaserprobada,peroenelcasodeunbinario,nohaynadaalocualenlazar.

Hayunaspocasanotacionesmasquesonútilesparaayudararustdocahacerlacosacorrectacuandopruebastucódigo:

///```ignore

///fnfoo(){

///

fnfoo(){}

ElLenguajedeProgramacionRust

99Documentación

Ladirectiva`ignore`lediceaRustqueignoreelcodigo.Estaeslaformaquecasinuncaquerrás,pueseslamasgenérica.Ensulugar,consideraelanotarcon`text`denosercodigo,ousar`#`sparaobtenerunejemplofuncionalquesolomuestralapartequeteinteresa.

```rust

///```should_panic

///assert!(false);

///

fnfoo(){}

`should_panic`ledicea`rustdoc`queelcódigodebecompilarcorrectamente,perosinlanecesidaddepasarunapruebademanerasatisfactoria.

```rust

///```no_run

///loop{

///println!("Hola,mundo");

///}

///

fnfoo(){}

Elatributo`no_run`compilaratucódigo,peronoloejecutara.Estoesimportanteparaejemploscomo"Heaquícomoiniciarunserviciodered,"elcualdebesasegurartequecompile,peropodríacausaruncicloinfinito!

###Documentandomódulos

Rustposeeotrotipodecomentariodedocumentación,`//!`.Estecomentarionodocumentaelsiguienteitem,estecomentaelitemqueloencierra.Enotraspalabras:

```rust

modfoo{

//!Estaesdocumentationparaelmodulo`foo`.

//!

//!#Examples

//...

}

Esaquíendondeveras//!usadomasamenudo:paradocumentacióndemódulos.Sitienesunmoduloenfoo.rs,frecuentementealaabrirsucódigoverasesto:

ElLenguajedeProgramacionRust

100Documentación

//!Unmoduloparausar`foo`s.

//!

//!Elmodulo`foo`contieneunmontondefuncionalidadblablabla

Estilodecomentariosdedocumentación

EchaunvistazoaelRFC505paraunlistadocompletodeconvencionesacercadelestiloyformatodeladocumentación(ingles)

OtradocumentaciónTodoestecomportamientofuncionaenarchivosnoRusttambién.DebidoaqueloscomentariossonescritosenMarkdown,frecuentementesonarchivos.md.

CuandoescribesdocumentaciónenarchivosMarkdown,nonecesitasprefijarladocumentaciónconcomentarios.Porejemplo:

///#Examples

///

///

///usestd::rc::Rc;//////letcinco=Rc::new(5);///```

fnfoo(){}

essolo

~~~markdown

#Examples

usestd::rc::Rc;

letcinco=Rc::new(5);

ElLenguajedeProgramacionRust

101Documentación

~~~

cuandoestaenunarchivoMarkdown.Solohayundetalle,losarchivosmarkdownnecesitanteneruntitulocomoeste:

```markdown

%Titulo

Estaesladocumentacióndeejemplo

Estalinea%deberestarubicadaenlaprimeralineadelarchivo.

atributosdocAunnivelmasprofundo,loscomentariosdedocumentaciónsonotraformadeescribiratributosdedocumentación:

///this

#fnfoo(){}

#[doc="this"]

#fnbar(){}

sonlomismoqueestos:

//!this

#![doc="///this"]

Noverasfrecuentementeesteatributosiendousadoparaescribirdocumentación,peropuedeserutilcuandoseestencambiandociertasopciones,oescribiendounamacro.

Re-exports

rustdocmostraraladocumentaciónparaunre-exportpublicoenamboslugares:

externcratefoo;

pubusefoo::bar;

Loanteriorcrearadocumentaciónparabardentrodeladocumentaciónparaelcratefoo,asícomoladocumentaciónparatucrate.Seralamismadocumentaciónenamboslugares.

ElLenguajedeProgramacionRust

102Documentación

Estecomportamientopuedesersuprimidoconno_inline:

externcratefoo;

#[doc(no_inline)]

pubusefoo::bar;

ControlandoHTML

PuedescontrolaralgunosaspectosdeelHTMLquerustdocgeneraatravésdelaversión#![doc]delatributo:

#![doc(html_logo_url="http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",

html_favicon_url="http://www.rust-lang.org/favicon.ico",

html_root_url="http://doc.rust-lang.org/")]

Estoconfiguraunaspocasopciones,conunlogo,favicon,yURLraíz.

Opcionesdegeneraciónrustdoctambiéncontieneunaspocasopcionesenlalineadecomandos,paramaspersonalización:

--html-in-headerFILE:incluyeelcontenidodeFILEalfinaldelasección<head>...</head>.--html-before-contentFILE:incluyeelcontenidodeFILEdespuésde<body>,antesdelcontenidorenderizado(incluyendolabarradebúsqueda).--html-after-contentFILE:incluyeelcontenidodeFILEdespuésdetodoelcontenidorenderizado.

NotadeseguridadElMarkdownenloscomentariosdedocumentaciónespuestosinprocesarenlapaginafinal.SecuidadosoconHTMLliteral:

///<script>alert(document.cookie)</script>

#fnfoo(){}

ElLenguajedeProgramacionRust

103Documentación

%Iteradores

Hablemosdeciclos.

RecuerdaselciclofordeRust?Heaquiunejemplo:

forxin0..10{

println!("{}",x);

}

AhoraquesabesmasRust,podemoshablarendetalleacercadecomoestecódigofunciona.Losrangos(el0..10)soniteradores.Uniteradoresalgoenloquepodemosllamarelmétodo.next()repetitivamente,yeliteradornosproporcionaunasecuenciadeelementos.

Porejemplo:

letmutrango=0..10;

loop{

matchrango.next(){

Some(x)=>{

println!("{}",x);

},

None=>{break}

}

}

Creamosunenlace(unavariable)aelrango,nuestroiterador.Luegoiteramosmedianteelcicloloop,conunmatchinterno.Dichomatchusaelresultadoderango.next(),quenosproporcionaunareferenciaaelsiguientevaloreneliterador.nextretornaunOption<i32>,enestecaso,queseráSome(i32)cuandotenemosunvaloryNonecuandonosquedemossinvalores.SiobtenemosSome(i32),loimprimimos,ysiobtenemosNone,rompemoselciclo,saliendodeelatravésdebreak.

Esteejemplodecódigoesbásicamenteelmismoquenuestraversiondeuncicloforfor.Elcicloforessolounaformapracticadeescribirunaconstrucciónloop/match/break.

Sinembargo,losciclosfornosonlaúnicacosaqueusaiteradores.EscribirtupropioiteradorimplicaimplementareltraitIterator.Sibienhacerloestafueradelámbitodeestaguía,Rustproveeaunnumerodeiteradoresútilesparallevaracabodiversastareas.Antesdehablardeeso,debemoshablaracercadeunanti-patron.Dichoanti-patronesusarrangosdelamaneraexpuestaanteriormente.

ElLenguajedeProgramacionRust

104Iteradores

Si,acabamosdehablaracercadecuancoolsonlosrangos.Perosontambiénmuyprimitivos.Porejemplo,sinecesitamositeraratravésdelcontenidodeunvector,podríamosestartentadosaescribiralgocomoesto:

letnums=vec![1,2,3];

foriin0..nums.len(){

println!("{}",nums[i]);

}

Estonoesestrictamentepeorqueusaruniterador.Sepuedeiterarenvectoresdirectamente,escribeesto:

letnums=vec![1,2,3];

fornumin&nums{

println!("{}",num);

}

Haydosrazonesparahacerlodeestamanera.Primero,expresaloquequeremosdemaneramasdirecta.Iteramosatravésdelvectorcompleto,envezdeiteraratravésdeindicesparaluegoindexarelvector.Segundo,estaversionesmaseficiente:enlaprimeraversiontendremoschequeosdelimitesextradebidoaqueusaindexado,nums[i].Enelsegundoejemploydebidoaqueconeliteradorcedemosunareferenciaacadaelementoalavez,hohaychequeodelimites.Estoesmuycomúneniteradores:podemosignorarchequeosdelimitesinnecesarios,sabiendoalmismotiempoqueestamosseguros.

Hayotrodetalleaquíquenoestael100%clarodebidoaelfuncionamientoprintln!.numesdetipo&i32.Unareferenciaauni32,nouni32.println!manejaeldereferenciamientopornosotros,esporelloquenolovemos.Estecódigotambiénescorrecto:

letnums=vec![1,2,3];

fornumin&nums{

println!("{}",*num);

}

Ahoraestamosdereferenciandoanumdeformaexplicita.Porque&numsnosdareferencias?Primeramente,porquelosolicitamosdemaneraexplicitacon&.Segundo,sinosdieraladataensimisma,tendríamosqueserdueñosdeella,locualimplicaríala

ElLenguajedeProgramacionRust

105Iteradores

creacióndeunacopiadeladataparadespuésdarnosesacopia.Conreferencias,soloestamoshaciendounpréstamo('borrowing')deunareferenciaaladata,yporellosolopasamosunareferencia,sinnecesidaddetransferirlapertenencia.

Entonces,ahoraquehemosestablecidoquelosrangosavecesnosonloquequeremos,hablemosdeloqueremos.

Haytresampliasclasesdecosasquesonrelevantes:iteradores,adaptadoresdeiteradoresyconsumidores.Heaquíalgunasdefiniciones:

iteradoresproporcionanunasequenciadevalores.adaptadoresdeiteradoresoperanenuniterador,produciendounnuevoiteradorconunasecuenciadiferentedesalida.consumidoresoperanenuniterador,produciendounconjuntofinaldevalores.

Hablemosprimeramenteacercadelosconsumidores,debidoaqueyahemosvistouniterador,losrangos.

ConsumidoresUnconsumidoroperaenuniterador,retornandoalgúntipodevalorovalores.Elconsumidormascomúnescollect().Estecódigonocompila,peromuestralaintención:

letuno_hasta_cien=(1..101).collect();

Comopuedesver,llamamoscollect()eneliterador.collect()tomatantosvalorescomoeliteradorleproporcione,retornandounacolecciónderesultados.Entonces,porqueestecódigonocompilara?Rustnopuededeterminarquetipodecosasquieresrecolectar,yesporellonecesitashacerlesaber.Estaeslaversionquecompila:

letuno_hasta_cien=(1..101).collect::<Vec<i32>>();

Sirecuerdas,lasintaxis::<>tepermitedarunaindicioacercadeltipo,ennuestrocasoestamosdiciendoquequeremosunvectordeenteros.Nosiempreesnecesariousareltipocompleto._tepermitirádarunindicioparcialacercadeltipo:

letuno_hasta_cien=(1..101).collect::<Vec<_>>();

Estodice"RecolectaenunVec<T>,porfavor,peroinfierequeesTpormi."._esporestarazónllamadoalgunasveces"marcadordeposicióndetipo".

ElLenguajedeProgramacionRust

106Iteradores

collect()eselconsumidormascomún,perohayotros.find()esunodeellos:

letmayores_a_cuarenta_y_dos=(0..100)

.find(|x|*x>42);

matchmayores_a_cuarenta_y_dos{

Some(_)=>println!("Tenemosalgunosnúmeros!"),

None=>println!("Noseencontraronnúmeros:("),

}

findrecibeunclosure,ytrabajaenunareferenciaacadaelementodeuniterador.Dichoclosureretornatruesielelementoeselqueestamosbuscandoyfalsedelocontrario.Debidoaquepodríamosnoencontrarunelementoquesatisfaganuestrocriterio,findretornaunOptionenlugardeunelemento.

Otroconsumidorimportanteesfold.Luceasi:

letsuma=(1..4).fold(0,|suma,x|suma+x);

fold(base,|acumulador,elemento|...).Tomadosargumentos:elprimeroesunelementollamadobase.Elsegundoesunclosurequeasuveztomadosargumentos:elprimeroesllamadoelacumulador,yelsegundoesunelemento.Encadaiteración,elclosureesllamado,yelresultadoesusadocomoelvalordelacumuladorenlasiguienteiteración.Enlaprimeraiteración,labaseeselvalordelacumulador.

Bien,esoesunpococonfuso.Examinemoslosvaloresdetodaslascosasenesteiterador:

base acumulador elemento resultadodelclosure

0 0 1 1

0 1 2 3

0 3 3 6

Hemosllamadoafold()conestosargumentos:

#(1..4)

.fold(0,|suma,x|suma+x);

Entonces,0esnuestrabase,sumaesnuestroacumulador,yxesnuestroelemento.Enlaprimeraiteración,asignamossuma0yxeselprimerelementodenuestrorango,1.Despuéssumamossumyxloquenosda0+1=1.Enlasegundaiteración,esevalorseconvierteenelvalordenuestroacumulador,sum,yelelementoeselsegundoelementodelrango,2.1+2=3ydeigualmaneraseconvierteenelvalordel

ElLenguajedeProgramacionRust

107Iteradores

acumuladorparalaultimaiteración.Enesaiteración,xeselultimoelemento,3,y3+3=6,resultadofinalparanuestrosuma.1+2+3=6,eseeselresultadoqueobtenemos.

Whew.foldpuedeserunpocoextrañoaprimeravista,perounavezhaceclick,puedesusarloentodoslados.Cadavezquetengasunalistadecosas,ynecesitesunúnicoresultado,foldesapropiado.

Losconsumidoressonimportantesdebidoaunapropiedadadicionaldelositeradoresdelaquenohemoshabladotodavia:pereza(laziness).Hablemosmasacercadelositeradoresyverasporquelosconsumidoressonimportantes.

IteradoresComohemosdichoantes,uniteradoresalgoenloquepodemosllamarelmétodo.next()repetidamente,yestenosdevuelveunasecuenciadeelementos.Debidoaquenecesitamosllamaraelmétodo,lositeradorespuedenserperezososynogenerartodoslosvaloresporadelantado.Estecódigo,porejemplo,nogeneralosnúmeros1-99.Ensulugarcreaunvalorquerepresentalasecuencia:

letnums=1..100;

Debidoaquenohicimosnadaconelrango,estenogenerolasecuencia.Agreguemosunconsumidor:

letnums=(1..100).collect::<Vec<i32>>();

Ahoracollect()requeriráqueelrangoproveaalgunosnúmeros,yenconsecuenciatendraquellevaracabolalabordegenerarlasecuencia.

Losrangossonunadelasdosformasbásicasdeiteradoresqueveras.Laotraesiter().iter()puedetransformarunvectorenuniteradorsimplequeproporcionaunelementoalavez:

letnums=vec![1,2,3];

fornuminnums.iter(){

println!("{}",num);

}

ElLenguajedeProgramacionRust

108Iteradores

Estosdositeradoresbásicosdeberianserdeutilidad.Existeniteradoresmasavanzados,incluyendoaquellosquesoninfinitos.

Suficienteacercadeiteradores,losadaptadoresdeiteradoressonelultimoconceptorelacionadoaiteradoresalquedebemoshacermención.Hagamoslo!

AdaptadoresdeiteradorLosadaptadoresdeiteradorestomanuniteradorylomodificandealgunamanera,produciendounonuevo.Elmassimpleesllamadomap:

(1..100).map(|x|x+1);

mapesllamadoenotroiterador,mapproduceuniteradornuevoenelquecadareferenciaaunelementoposeeelclosurequesehaproporcionadocomoargumento.Elcódigoanteriornosdarálosnúmeros2-100,Bueno,casi!Sicompilaselejemplo,obtendrásunaadvertencia:

warning:unusedresultwhichmustbeused:iteratoradaptorsarelazyand

donothingunlessconsumed,#[warn(unused_must_use)]onbydefault

(1..100).map(|x|x+1);

^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Laperezaatacadenuevo!Eseclosurenuncaseejecutara.Esteejemplonoimprimeningúnnumero:

(1..100).map(|x|println!("{}",x));

Siestasintentantoejecutarunclosureenuniteradorparaobtenersusefectoscolaterales(side-effects)usaunfor.

foriin(1..).take(5){

println!("{}",i);

}

Estoimprimira:

ElLenguajedeProgramacionRust

109Iteradores

1

2

3

4

5

filter()esunadaptadorquetomaunclosurecomoargumento.Dichoclosureretornatrueofalse.Elnuevoiteradorquefilter()producesoloelementosparalosqueelclosureretornatrue:

foriin(1..100).filter(|&x|x%2==0){

println!("{}",i);

}

Estoimprimirátodoslosnúmerosparesentreunoycien.(Notaquedebidoaquefilternoconsumeloselementosqueestánsiendoiterados,aesteselepasaunareferenciaacadaelemento,debidoaello,elpredicadousaelpatron&xparaextraerelentero.)

(1..)

.filter(|&x|x%2==0)

.filter(|&x|x%3==0)

.take(5)

.collect::<Vec<i32>>();

Loanteriorteunvectorconteniendo6,12,18,24,y30.

Estaesunapequeñamuestradelascosasenlascualeslositeradores,adaptadoresdeiteradores,yconsumidorespuedenayudarte.Existeunavariedaddeiteradoresrealmenteútiles,juntoalhechoquepuedesescribirtuspropios.Lositeradoresproporcionanunamanerasegurayeficientedemanipulartodotipodelistas.Sonunpocoinusualesaprimeravista,perosijuegasunpococonellos,quedarasenganchado.Paraunalistadelosdiferentesiteradoresyconsumidoresechaunvistazoaladocumentaciondelmoduloiterator(ingles).

ElLenguajedeProgramacionRust

110Iteradores

%Concurrencia

Laconcurrenciayelparalelismosondostópicosincreíblementeimportantesenlascienciasdelacomputación,tambiénsonuntópicocalienteenlaindustriahoyendía.Lascomputadorascadavezposeenmasymasnúcleos,aunasí,todavíaalgunosdesarrolladoresnoestánpreparadosparautilizarnoscompletamente.

LaseguridadenelmanejodememoriadeRusttambiénaplicaasuhistoriadeconcurrencia.Inclusolosprogramasconcurrentesdebensersegurosenelmanejodememoria,sincondicionesdecarrera.ElsistemadetiposdeRustestaalaalturadeldesafío,ytedotadepoderosasvíaspararazonaracercadecódigoconcurrenteentiempodecompilación.

AntesdequehablemosdelascaracterísticasdeconcurrenciaquevienenconRust,esimportanteentenderalgo:Rustesdebajonivel,suficientementebajoalpuntoquetodasestasfacilidadesestánimplementadasenlabibliotecaestándar.EstosignificaquesinotegustaalgúnaspectodelamaneraenlaqueRustmanejalaconcurrencia,puedesimplementarunaformaalternativadehacerlo.mioesunvivoejemplodeesteprincipioenacción.

Bases:SendySyncLaconcurrenciaesalgosobreloqueesdifícilrazonar.EnRusttenemosunpoderoso,sistemadetiposestáticoquenosayudaarazonaracercadenuestrocódigo.Comotal,Rustnosproveededostraitsparaayudarnosadarlesentidoacódigoquepuedaposiblementeserconcurrente.

Send

ElprimertraitdelcualhablaremosesSend(ingles).CuandountipoTimplementaSend,leindicaalcompiladorquealgodeestetipopuedetransferirlapertenenciaentrehilosdeformasegura.

Estoesimportanteparahacercumplirciertasrestricciones.Porejemplositenemosuncanal,conectandodoshilos,deberíamosquerertransferiralgunosdatosaelotrohilotravésdelcanal.Porlotanto,nosaseguramosdequeSendhayasidoimplementadoparaesetipo.

Demaneraopuesta,siestamosenvolviendounabibliotecaconFFIquenoesthreadsafe,nodeberíamosquererimplementarSend,demaneratalqueelcompiladornosayudeaasegurarnosqueestanopuedaabandonarelhiloactual.

ElLenguajedeProgramacionRust

111Concurrencia

Sync

ElsegundodeestostraitsesllamadoSync(ingles).CuandountipoTimplementaSync,leindicaalelcompiladorquealgodeestetiponotieneposibilidaddeintroducirinseguridadenmemoriacuandoesusadodemaneraconcurrentepormultipleshilosdeejecución.

Porejemplo,compartirdatainmutableconunacuentadereferenciasatómicaesthreadsafe.Rustproveedichotipo,Arc<T>,elcualimplementaSync,yesporelloqueessegurodecompartirentrehilos.

Estosdostraitstepermitenusarelsistemadetiposparahacergarantíasfuertesacercadelaspropiedadesdetucódigobajoconcurrencia.Enprimerlugar,antesdedemostrarporque,necesitamosaprendercomocrearunprogramaconcurrenteenRust!

HilosLabibliotecaestándardeRustproveeunabibliotecaparaelmanejodehilos,quetepermiteejecutarcódigorustdeformaparalela.Heaquiunejemplobasicodelusodestd::thread:

usestd::thread;

fnmain(){

thread::spawn(||{

println!("Holadesdeunhilo!");

});

}

Elmétodothread::spawn()aceptaunclosurecomoargumento,closurequeesejecutadoenunnuevohilo.thread::spawn()retornaunhandleaelnuevohilo,quepuedeserusadoparaesperaraqueelhilofinaliceyluegoextraersuresultado:

usestd::thread;

fnmain(){

lethandle=thread::spawn(||{

"Holadesdeunhilo!"

});

println!("{}",handle.join().unwrap());

}

ElLenguajedeProgramacionRust

112Concurrencia

Muchoslenguajesposeenlahabilidaddeejecutarhilos,peroessalvajementeinseguro.Existenlibrosenterosacercadecomoprevenirloserroresqueocurrencomoconsecuenciadecompartirestadomutable.Rustayudaconsusistemadetipos,previniendocondicionesdecarreraentiempodecompilación.Hablemosacercadecomoefectivamentepuedescompartircosasentrehilos.

EstadoMutableCompartidoSeguroDebidoaelsistemadetiposdeRust,tenemosunconceptoquesuenacomounamentira:"estadomutablecompartidoseguro".Muchosprogramadoresconcuerdanenqueelestadomutablecompartidoesmuy,muymalo.

Alguiendijoalgunavez:

Elestadomutablecompartidoeslaraízdetodamaldad.Lamayoríadeloslenguajesintentanlidiarconesteproblemaatravésdelaparte'mutable',peroRustloenfrentaresolviendolaparte'compartida'.

Lamismapertenenciaqueprevieneelusoincorrectodeapuntadorestambiénayudaaeliminarlascondicionesdecarrera,unodelospeoresbugsrelacionadosconconcurrencia.

Comoejemplo,unprogramaRustquetendríaunacondicióndecarreraenmuchoslenguajes.EnRustnocompilaría:

usestd::thread;

fnmain(){

letmutdata=vec![1u32,2,3];

foriin0..3{

thread::spawn(move||{

data[i]+=1;

});

}

thread::sleep_ms(50);

}

Loanteriorproduceunerror:

8:17error:captureofmovedvalue:`data`

data[i]+=1;

^~~~

ElLenguajedeProgramacionRust

113Concurrencia

Enestecaso,sabemosquenuestrocódigodeberiaserseguro,peroRustnoestasegurodeello.Enrealidadnoesseguro,situviéramosunareferenciaadataencadahilo,yelhilotomapertenenciadelareferencia,tendríamostresdueños!Esoestamal.PodemosarreglarestoatravésdelusodeltipoArc<T>,unapuntadorconconteoatómicodereferencias.Laparte'atómico'significaqueessegurocompartirloentrehilos.

Arc<T>asumeunapropiedadmasacercadesucontenidoparaasegurarsequeessegurocompartirloentrehilos:asumequesucontenidoesSync.Peroennuestrocaso,queremosmutarelvalor.Necesitamosuntipoquepuedaasegurarsequesolounapersonaalavezpuedamutarloqueestedentro.Paraeso,podemosusareltipoMutex<T>.Heaquílasegundaversiondenuestrocódigo.Aunnofunciona,peroporunarazóndiferente:

usestd::thread;

usestd::sync::Mutex;

fnmain(){

letmutdata=Mutex::new(vec![1u32,2,3]);

foriin0..3{

letdata=data.lock().unwrap();

thread::spawn(move||{

data[i]+=1;

});

}

thread::sleep_ms(50);

}

Heaquielerror:

:9:9:9:22error:thetrait`core::marker::Send`isnotimplementedforthetype`std::sync::mutex::MutexGuard<'_,collections::vec::Vec

:11thread::spawn(move||{

^~~~~~~~~~~~~

:9:9:9:22note:`std::sync::mutex::MutexGuard<'_,collections::vec::Vec>`cannotbesentbetweenthreadssafely

:11thread::spawn(move||{

^~~~~~~~~~~~~

Loves,Mutexposeeunmetodolockqueposeeestafirma:

fnlock(&self)->LockResult>

DebidoaqueSendnoestaimplementadoparaMutexGuard<T>,nopodemostransferirelMutexGuard<T>entrehilos,loquesetraduceenelerror.

PodemosusarArc<T>paracorregirelerror.Heaquilaversionfuncional:

ElLenguajedeProgramacionRust

114Concurrencia

usestd::sync::{Arc,Mutex};

usestd::thread;

fnmain(){

letdata=Arc::new(Mutex::new(vec![1u32,2,3]));

foriin0..3{

letdata=data.clone();

thread::spawn(move||{

letmutdata=data.lock().unwrap();

data[i]+=1;

});

}

thread::sleep_ms(50);

}

Ahorallamamosclone()ennuestroArc,locualincrementaelcontadorinterno.Estehandleesentoncesmovidodentrodelnuevohilo.Examinemoselcuerpodelhilomasdecerca:

#usestd::sync::{Arc,Mutex};

#usestd::thread;

#fnmain(){

#letdata=Arc::new(Mutex::new(vec![1u32,2,3]));

#foriin0..3{

#letdata=data.clone();

thread::spawn(move||{

letmutdata=data.lock().unwrap();

data[i]+=1;

});

#}

#thread::sleep_ms(50);

#}

Primero,llamamoslock(),locualobtieneelbloqueodeexclusionmutua.Debidoaqueestaoperaciónpuedefallar,estemétodoretornaunResult<T,E>,ydebidoaqueestoessolounejemplo,hacemosunwrap()enelparaobtenerunareferenciaaladata.Códigorealtendríaunmanejodeerroresmasrobustoenestelugar.Somoslibresdemutarlo,puestoquetenemoselbloqueo.

Porultimo,mientrasqueloshilosseejecutan,esperamosporlaculminacióndeuntemporizadorcorto.Estonoesideal:pudimoshaberescogidountiemporazonableparaesperarperolomasprobableesqueesperemosmasdelonecesario,onolosuficiente,dependiendodecuantotiempoloshilostomanparaterminarlacomputacióncuandoelprogramacorre.

ElLenguajedeProgramacionRust

115Concurrencia

UnaalternativamasprecisaaeltemporizadorseriaelusodeunodelosmecanismosproporcionadosporlabibliotecaestándardeRustparalasincronizaciónentrehilos.Hablemosdeellos:loscanales.

CanalesHeaquíunaversionnuestrocódigoqueusacanalesparalasincronización,enlugardeesperarporuntiempoespecifico:

usestd::sync::{Arc,Mutex};

usestd::thread;

usestd::sync::mpsc;

fnmain(){

letdata=Arc::new(Mutex::new(0u32));

let(tx,rx)=mpsc::channel();

for_in0..10{

let(data,tx)=(data.clone(),tx.clone());

thread::spawn(move||{

letmutdata=data.lock().unwrap();

*data+=1;

tx.send(());

});

}

for_in0..10{

rx.recv();

}

}

Hacemosusodelmétodompsc::channel()paraconstruiruncanalnuevo.Enviamos(atravésdesend)unsimple()atravésdelcanal,yluegoesperamosporelregresodiezdeellos.

Mientrasqueestecanalestasoloenviandounasenalgenérica,podemosenviarcualquierdataqueseaSendatravésdelcanal!

ElLenguajedeProgramacionRust

116Concurrencia

usestd::thread;

usestd::sync::mpsc;

fnmain(){

let(tx,rx)=mpsc::channel();

for_in0..10{

lettx=tx.clone();

thread::spawn(move||{

letrespuesta=42u32;

tx.send(respuesta);

});

}

rx.recv().ok().expect("Nosehapodidorecibirlarespuesta");

}

Unu32esSendporquepodemoshacerunacopiadeel.Entoncescreamosunhilo,ylesolicitamosquecalculelarespuesta,esteluegonosenvíalarespuestaderegreso(usandosend())atravésdelcanal.

PanicosUnpanic!causaralafinalizaciónabrupta(crash)delhilodeejecuciónactual.PuedesusarloshilosdeRustcomounmecanismodeaislamientosencillo:

usestd::thread;

letresultado=thread::spawn(move||{

panic!("ups!");

}).join();

assert!(resultado.is_err());

NuestroThreadnosdevuelveunResult,elcualnospermitechequearsielhilohahechopánicoono.

ElLenguajedeProgramacionRust

117Concurrencia

%ManejodeErrores

Losplanesmejorestablecidosporratonesyhombresamenudosetuercen."TaeaMoose",RobertBurns

Algunasveces,lascosassimplementesalenmal.Esimportantetenerunplanparacuandoloinevitablesuceda.Rustposeeunsoportericoparaelmanejodeerroresquepodrían(seamoshonestos:ocurrirán)ocurrirentusprogramas.

Existendostiposdeerroresquepuedenocurrirentusprogramas:fallasypánicos.Hablaremosdelasdiferenciasentrelosdos,yluegodiscutiremoscomomanejarcadauno.Despuésdiscutiremoscomopromoverfallasapánicos.

Fallavs.PánicoRustusadostérminosparadiferenciarentrelasdosformasdeerror:falla,ypánico.Unafallaesunerrordelcualnospodemosrecuperardealgunamanera.Unpánicoesunerrorirrecuperable.

Quequeremosdecircon"recuperar"?Bueno,enlamayoríadeloscasos,laposibilidaddeunerroresesperada.Porejemplo,consideralafunciónparse:

"5".parse();

Estemétodoconvierteunacadenadecaracteresaotrotipo.Perodebidoaqueesunacadenadecaracteres,nosepuedeestarsegurodequelaconversionefectivamentetengaéxito.Porejemplo,aquedeberíaserconvertidoesto?:

"hola5mundo".parse();

Loanteriornofunciona.Sabemosqueelmétodoparse()solotendráéxitoparaalgunasentradas.Esuncomportamientoesperado.Esporelloquellamamosaesteerrorunafalla.

Porotrolado,algunasveces,hayerroresquesoninesperados,odeloscualesnonospodemosrecuperar.Unejemploclásicoesunassert!:

#letx=5;

assert!(x==5);

ElLenguajedeProgramacionRust

118ManejodeErrores

Usamosassert!paradeclararquealgoescierto(true).Siladeclaraciónnoesverdad,entoncesalgoestamuymal.Suficientementemalcomoparanopodercontinuarlaejecuciónenelestadoactual.Otroejemploeselusodelamacrounreachable!():

useEvento::NuevoLanzamiento;

enumEvento{

NuevoLanzamiento,

}

fnprobabilidad(_:&Evento)->f64{

//laimplementaciónrealseriamascompleja,porsupuesto

0.95

}

fnprobabilidad_descriptiva(evento:Evento)->&'staticstr{

matchprobabilidad(&evento){

1.00=>"cierto",

0.00=>"imposible",

0.00...0.25=>"muypocoprobable",

0.25...0.50=>"pocoprobable",

0.50...0.75=>"probable",

0.75...1.00=>"muyprobable",

}

}

fnmain(){

println!("{}",probabilidad_descriptiva(NuevoLanzamiento));

}

Loanteriorresultaraenunerror:

error:non-exhaustivepatterns:`_`notcovered[E0004]

Sibiensabemosquehemoscubiertotodosloscasosposibles,Rustnopuedesaberlo.Nosabecualeslaprobabilidadentre0.0y1.0.Esporelloqueagregamosotrocaso:

ElLenguajedeProgramacionRust

119ManejodeErrores

useEvento::NuevoLanzamiento;

enumEvento{

NuevoLanzamiento,

}

fnprobabilidad(_:&Evento)->f64{

//laimplementaciónrealseriamascompleja,porsupuesto

0.95

}

fnprobabilidad_descriptiva(evento:Evento)->&'staticstr{

matchprobabilidad(&evento){

1.00=>"cierto",

0.00=>"imposible",

0.00...0.25=>"muypocoprobable",

0.25...0.50=>"pocoprobable",

0.50...0.75=>"probable",

0.75...1.00=>"muyprobable",

_=>unreachable!()

}

}

fnmain(){

println!("{}",probabilidad_descriptiva(NuevoLanzamiento));

}

Nuncadeberíamosalcanzarelcaso_,debidoaestohacemosusodelamacroparaindicarlo.unreachable!()produceuntipodiferentedeerrorqueResult.Rustllamaaesetipodeerrorespánicos.

Manejandoerrorescon Optiony ResultLamaneramassimpledeindicarqueunafunciónpuedefallaresusandoeltipoOption<T>.Porejemplo,elmétodofindenlascadenasdecaracteresintentalocalizarunpatrónenlacadena,retornaunOption:

lets="foo";

assert_eq!(s.find('f'),Some(0));

assert_eq!(s.find('z'),None);

Estoesapropiadoparacasossimples,perononosdamuchainformaciónenelcasodeunafalla.Quetalsiquisiéramossaberelporquelafunciónfalló?Paraello,podemosusareltipoResult<T,E>.Queluceasí:

ElLenguajedeProgramacionRust

120ManejodeErrores

enumResult<T,E>{

Ok(T),

Err(E)

}

EstaenumesproporcionadaporRust,esporelloquenonecesitasdefinirlasideseashacerusodeella.LavarianteOk(T)representaéxito,ylavarianteErr(E)representaunafalla.RetornarunResultenlugardeunOptionesrecomendableparalamayoríadeloscasosnotriviales:

HeaquíunejemplodelusodeResult:

#[derive(Debug)]

enumVersion{Version1,Version2}

#[derive(Debug)]

enumErrorParseo{LongitudCabeceraInvalida,VersionInvalida}

fnparsear_version(cabecera:&[u8])->Result<Version,ErrorParseo>{

ifcabecera.len()<1{

returnErr(ErrorParseo::LongitudCabeceraInvalida);

}

matchcabecera[0]{

1=>Ok(Version::Version1),

2=>Ok(Version::Version2),

_=>Err(ErrorParseo::LongitudCabeceraInvalida)

}

}

fnmain(){

letversion=parsear_version(&[1,2,3,4]);

matchversion{

Ok(v)=>{

println!("trabajandoconlaversion:{:?}",v);

}

Err(e)=>{

println!("errorparseandocebecera:{:?}",e);

}

}

}

Estafunciónhaceusodeunenum,ErrorParseo,paraenumerarloserroresquepuedenocurrir.

EltraitDebugeselquenospermiteimprimirelvalordelenumusandolaoperacióndeformato{:?}.

ElLenguajedeProgramacionRust

121ManejodeErrores

Erroresnorecuperablescon panic!Enelcasodeunerrorinesperadodelcualnosepuedarecuperar,lamacropanic!seutilizaparainducirunpánico.Dichopánicoterminaraabruptamenteelhiloactualdeejecuciónproporcionandounmensajedeerror:

panic!("boom");

resultaen

thread'

'panickedat'boom',hello.rs:2

cuandoloejecutas.

Debidoaqueestassituacionessonrelativamenteraras,usalospánicosconmoderación.

PromoviendofallasapánicosEnciertascircunstancias,aunsabiendoqueunafunciónpuedefallar,podríamosquerertratarlafallacomounpánico.Porejemplo,io::stdin().read_line(&mutbuffer)retornaunResult<usize>,cuandohayunerrorleyendolalinea.Estonospermitemanejaryposiblementerecuperarnosencasodeerror.

Sinoqueremosmanejarelerror,yensulugarsimplementeabortarelprograma,podemosusarelmétodounwrap():

io::stdin().read_line(&mutbuffer).unwrap();

unwrap()haráunpánico(panic!)sielResultesErr.Estobásicamentedice"Dameelvalor,ysialgosalemal,simplementeabortalaejecución".Estoesmenosconfiablequehacermatchenelerrorytratarderecuperarnos,peroalmismotiempoessignificativamentemascorto.Algunasveces,laterminaciónabruptaesapropiada.

Hayunamaneraquedehacerloanteriorqueesunpocomejorqueunwrap():

letmutbufer=String::new();

letbytes_leidos=io::stdin().read_line(&mutbufer)

.ok()

.expect("Falloalleerlinea");

ElLenguajedeProgramacionRust

122ManejodeErrores

ok()convierteelResultenunOption,yexpect()hacelomismoqueunwrap(),perorecibeunmensajecomoargumento.Estemensajeespasadoaelpanic!subyacente,proporcionandounmejormensajedeerror.

Usando try!CuandoescribimoscódigoquellamaamuchasfuncionesqueretornaneltipoResult,elmanejodeerroressepuedetornartedioso.Lamacrotry!escondealgodeelcódigorepetitivocorrespondientealapropagacióndeerroresenlapiladellamadas.

try!reemplaza:

usestd::fs::File;

usestd::io;

usestd::io::prelude::*;

structInfo{

nombre:String,

edad:i32,

grado:i32,

}

fnescribir_info(info:&Info)->io::Result<()>{

letmutarchivo=File::create("mis_mejores_amigos.txt").unwrap();

ifletErr(e)=writeln!(&mutarchivo,"nombre:{}",info.nombre){

returnErr(e)

}

ifletErr(e)=writeln!(&mutarchivo,"edad:{}",info.edad){

returnErr(e)

}

ifletErr(e)=writeln!(&mutarchivo,"grado:{}",info.rgrado){

returnErr(e)

}

returnOk(());

}

Con:

ElLenguajedeProgramacionRust

123ManejodeErrores

usestd::fs::File;

usestd::io;

usestd::io::prelude::*;

structInfo{

nombre:String,

edad:i32,

grado:i32,

}

fnescribir_info(info:&Info)->io::Result<()>{

letmutarchivo=File::create("mis_mejores_amigos.txt").unwrap();

try!(writeln!(&mutarchivo,"nombre:{}",info.nombre));

try!(writeln!(&mutarchivo,"edad:{}",info.edad));

try!(writeln!(&mutarchivo,"grado:{}",info.grado));

returnOk(());

}

Envolverunaexpresióncontry!resultaraenelvalor(Ok)exitosodesenvuelto,amenosqueelresultadoseaErr,casoenelcualErresretornadodemaneratempranaporlafunciónqueenvuelvealtry.

Esimportantehacermenciónaelhechodequesolopuedesusartry!desdeunafunciónqueretornaunResult,loquesetraduceenquenopuedesusartry!dentrodemain(),debidoaquemain()noretornanada.

try!haceusodeFrom<Error>(ingles)paradeterminarqueretornarenelcasodeerror.

ElLenguajedeProgramacionRust

124ManejodeErrores

%InterfazdeFuncionesForáneas/Externas

IntroducciónEstaguíausaralabibliotecadecompresión/descompresiónsnappycomointroducciónalaescrituradebindingsacódigoexterno.RustactualmentenopuedellamarcódigoenunabibliotecaC++demaneradirecta,perosnappyincluyeunainterfazenC(documentadaensnappy-c.h).

Elsiguienteesunejemplomininodecomollamarunafunciónforáneaquecompilaraasumiendoquesnappyestainstalada:

##![feature(libc)]

externcratelibc;

uselibc::size_t;

#[link(name="snappy")]

extern{

fnsnappy_max_compressed_length(source_length:size_t)->size_t;

}

fnmain(){

letx=unsafe{snappy_max_compressed_length(100)};

println!("longitudmaximadeunbufferthe100bytescomprimido:{}",x);

}

Elbloqueexternesunalistadefirmasdefunciónenunabibliotecaforánea,enestecasoconlainterfazbinariadeaplicaciónC(ABIeningles)delaplataforma.Elatributo#[link(...)]esusadoparainstruiralenlazadoraenlazarconlabibliotecasnappydemodoquelossímbolospuedanserresueltos.

Seasumequelasinterfacesafuncionesforáneassoninseguras,esporelloquelasllamadasaellasdebenestardentrodeunbloqueunsafe{}comounapromesaalcompiladordequetodolocontenidoenelesrealmenteseguro.BibliotecasenCaalgunasvecesexponeninterfacesquenosonthread-safe,ycasicualquierfunciónquetomaunapuntadorcomoargumentonoesvalidaparatodaslasentradasposiblesdebidoaqueelapuntadorpodríaserunapuntadorcolgante,ylosapuntadoresplanosquedanporfueradelmodelodememoriaseguradeRust.

Cuandosedeclaranlostiposdelosargumentosdeunafunciónforánea,elcompiladordeRustnopuedechequearsiladeclaraciónescorrecta,aconsecuenciadeesto,especificarlademaneracorrectaformapartedemantenerelbindingcorrectoentiempodeejecución.

ElbloqueexternpuedeserextendidoparacubrirlaAPIcompletadesnappy:

ElLenguajedeProgramacionRust

125FFI

##![feature(libc)]

externcratelibc;

uselibc::{c_int,size_t};

#[link(name="snappy")]

extern{

fnsnappy_compress(input:*constu8,

input_length:size_t,

compressed:*mutu8,

compressed_length:*mutsize_t)->c_int;

fnsnappy_uncompress(compressed:*constu8,

compressed_length:size_t,

uncompressed:*mutu8,

uncompressed_length:*mutsize_t)->c_int;

fnsnappy_max_compressed_length(source_length:size_t)->size_t;

fnsnappy_uncompressed_length(compressed:*constu8,

compressed_length:size_t,

result:*mutsize_t)->c_int;

fnsnappy_validate_compressed_buffer(compressed:*constu8,

compressed_length:size_t)->c_int;

}

#fnmain(){}

CreandounainterfazseguraLainterfazplanaenCnecesitaserenvueltaconelobjetivodeproveerseguridadenelmanejodememoriaasícomoelusodeconceptosdealtoniveltalescomovectores.Unabibliotecapuedeescogerentreexponerlainterfazseguradealtonivelyesconderlosdetallesinsegurosinternos.

Envolverlasfuncionesqueesperanbufersinvolucraelusodeelmoduloslice::rawparalamanipulacióndevectoresRustcomoapuntadoresamemoria.LosvectoresdeRustestángarantizadosaserunbloquedememoriacontiguo.Lalongitudeselnumerodeelementosactualmentecontenidos,ylacapacidadeseltamañototaldelamemoriaasignada.Lalongitudesmenoroigualalacapacidad.

##![feature(libc)]

#externcratelibc;

#uselibc::{c_int,size_t};

#unsafefnsnappy_validate_compressed_buffer(_:*constu8,_:size_t)->c_int{0}

#fnmain(){}

pubfnvalidar_buffer_comprimido(src:&[u8])->bool{

unsafe{

snappy_validate_compressed_buffer(src.as_ptr(),src.len()assize_t)==0

}

}

ElLenguajedeProgramacionRust

126FFI

Lafunciónenvoltoriovalidar_buffer_comprimidohaceusodeunbloqueunsafe,perohacelagarantíadequellamarlaesseguraparatodaslasentradasatravésdelaexclusiondelunsafedelafirmadelafunción.

Lasfuncionessnappy_compressysnappy_uncompresssonmascomplejas,debidoaqueunbufertienequeserasignadoparamantenerlasalida.

Lafunciónsnappy_max_compressed_lengthpuedeserusadaparaasignarunvectorconlacapacidadmaximarequeridaparaalmacenarlasalidacomprimida.Elvectorentoncespuedeserpasadoalafunciónsnappy_compresscomounparámetrodesalida.Unparámetrodesalidaestambiénpasadoparaobtenerlalongitudrealdespuésdelacompresiónparaasignarlalongitud.

##![feature(libc)]

#externcratelibc;

#uselibc::{size_t,c_int};

#unsafefnsnappy_compress(a:*constu8,b:size_t,c:*mutu8,

#d:*mutsize_t)->c_int{0}

#unsafefnsnappy_max_compressed_length(a:size_t)->size_t{a}

#fnmain(){}

pubfncomprimir(orig:&[u8])->Vec<u8>{

unsafe{

letlong_orig=orig.len()assize_t;

letporig=orig.as_ptr();

letmutlong_dest=snappy_max_compressed_length(long_orig);

letmutdest=Vec::with_capacity(long_destasusize);

letpdest=dest.as_mut_ptr();

snappy_compress(porig,long_orig,pdest,&mutlong_dest);

dest.set_len(long_destasusize);

dest

}

}

Ladescompresionessimilar,debidoaquesnappyalmacenalalongituddescomprimidacomopartedelformatodecompresiónysnappy_uncompressed_lengthobtendráeltamañoexactodelbuferrequerido.

ElLenguajedeProgramacionRust

127FFI

##![feature(libc)]

#externcratelibc;

#uselibc::{size_t,c_int};

#unsafefnsnappy_uncompress(compressed:*constu8,

#compressed_length:size_t,

#uncompressed:*mutu8,

#uncompressed_length:*mutsize_t)->c_int{0}

#unsafefnsnappy_uncompressed_length(compressed:*constu8,

#compressed_length:size_t,

#result:*mutsize_t)->c_int{0}

#fnmain(){}

pubfndescomprimir(orig:&[u8])->Option<Vec<u8>>{

unsafe{

letlong_orig=orig.len()assize_t;

letporig=orig.as_ptr();

letmutlong_dest:size_t=0;

snappy_uncompressed_length(porig,long_orig,&mutlong_dest);

letmutdest=Vec::with_capacity(long_destasusize);

letpdest=dest.as_mut_ptr();

ifsnappy_uncompress(porig,long_orig,pdest,&mutlong_dest)==0{

dest.set_len(long_destasusize);

Some(dest)

}else{

None//SNAPPY_INVALID_INPUT

}

}

}

Comoreferencia,losejemplosusadosaquíestándisponiblestambiéncomounabibliotecaenGithub.

DestructoresLabibliotecasexternasusualmentetransfierenlapertenenciadelosrecursosaelcódigollamador.Cuandoestoocurre,debemosusarlosdestructoresdeRustparaproveerseguridadylagarantíadelaliberacióndedichosrecursos(especialmenteenelcasodeunpánico).

Paramasinformaciónacercadelosdestructores,echaunvistazoalaseccióndeltraitDrop.

CallbacksdesdecódigoCafunciones

ElLenguajedeProgramacionRust

128FFI

RustAlgunasbibliotecasexternasrequierenelusodecallbacksparareportardevueltasuestadoodataparcialaelllamador.EsposiblepasarfuncionesdefinidasenRustaunabibliotecaexterna.ElrequerimientoparaestoesquelafuncióncallbackestemarcadacomoexternyconlaconvencióndellamadascorrectaparahacerposiblesullamadodesdecódigoC.

LafuncióncallbackpuedeentoncesserenviadaatravésdeunallamadaderegistroalabibliotecaenCysuposteriorinvocacióndesdeallá.

Unejemplobásicoes:

CódigoRust:

externfncallback(a:i32){

println!("HesidollamadodesdeCconelvalor{0}",a);

}

#[link(name="extlib")]

extern{

fnregistrar_callback(cb:externfn(i32))->i32;

fndisparar_callback();

}

fnmain(){

unsafe{

registrar_callback(callback);

disparar_callback();//Disparaelcallback

}

}

CódigoC:

typedefvoid(*callback_rust)(int32_t);

callback_rustcb;

int32_tregistrar_callback(callback_rustcallback){

cb=callback;

return1;

}

voiddisparar_callback(){

cb(7);//Llamaraacallback(7)enRust

}

Enesteejemploelmain()deRustllamaraadisparar_callback()enC,queasuvezllamaradevueltaacallback()enRust.

ElLenguajedeProgramacionRust

129FFI

ApuntandocallbacksaobjetosRustElejemploanteriordemostrócomounafunciónglobalpuedeserllamadadesdecódigoenC.SiembragoavecessedeseaqueelcallbackapunteaunobjetoRustespecial.EstepodríaserelobjetoquerepresentaelenvoltorioparaelobjetorespectivoenC.

TodoestopuedeserlogradoatravésdelpasodeunapuntadorplanoalabibliotecaenC.LabibliotecaenCentoncespuedeincluirelapuntadoraelobjetoRustenlanotificación.EstopermitiráaelcallbackaccederdemanerainseguraelobjetoRustreferenciado.

CódigoRust:

#[repr(C)]

structObjetoRust{

a:i32,

//otrosmiembros

}

extern"C"fncallback(objetivo:*mutObjetoRust,a:i32){

println!("HesidollamadodesdeCconelvalor{0}",a);

unsafe{

//ActualizaelvalorenObjetoRustconelvalorrecibidodesdeelcallback

(*objetivo).a=a;

}

}

#[link(name="extlib")]

extern{

fnregistrar_callback(objetivo:*mutObjetoRust,

cb:externfn(*mutObjetoRust,i32))->i32;

fndisparar_callback();

}

fnmain(){

//Creandoelobjetoqueserareferenciadoenelcallback

letmutobjeto_rust=Box::new(ObjetoRust{a:5});

unsafe{

registrar_callback(&mut*objeto_rust,callback);

disparar_callback();

}

}

CódigoC:

ElLenguajedeProgramacionRust

130FFI

typedefvoid(*callback_rust)(void*,int32_t);

void*objetivo_cb;

callback_rustcb;

int32_tregistrar_callback(void*objetivo_callback,callback_rustcallback){

objetivo_cb=objetivo_callback;

cb=callback;

return1;

}

voiddisparar_callback(){

cb(objetivo_cb,7);//Llamareacallback(&ObjetoRust,7)inRust

}

CallbacksAsíncronosEnlosejemplosanterioresloscallbackssoninvocadoscomounareaccióndirectaaunallamadaafunciónalabibliotecaexternaenC.ElcontrolsobreelhiloactualescambiadodeRustaCparalaejecucióndelcallback,peroalfinalelcallbackesejecutadoenelmismohiloquellamoalafunciónquedisparoelcallback.

Lascosassevuelvenmascomplicadascuandolabibliotecaexternacreasuspropioshiloseinvocaloscallbacksdesdeellos.EnestoscasoselaccesoalasestructurasdedatosRustdentrodeloscallbacksesespecialmenteinseguroymecanismosdesincronizaciónapropiadosdebenserusados.Ademasdelosmecanismosclásicosdesincronizacióncomomutexes,unaposibilidadenRusteselusodecanales(enstd::sync::mpsc)paratransferirdatadesdeelhiloCqueinvocoelcallbackaunhiloRust.

SiuncallbackasíncronotienecomoobjetivounobjetoespecialenelespaciodedireccionesdeRustesabsolutamentenecesariotambiénqueningúnotrocallbackseaejecutadosporlabibliotecaenCdespuésdequeelrespectivoobjetoRusthayasidodestruido.Estopuedeserllevadoacabode-registrandoelcallbackeneldestructordelobjetoydiseñandolabibliotecademaneratalquegaranticequeningúncallbackseraejecutadodespuésdelade-registracion.

EnlaceElatributolinkenbloquesexternproporcionaelbloquedeconstrucciónbasicoparainstruirarustccomoenlazarconbibliotecasnativas.Hoyendiahaydosformasdelatributolink:

#[link(name="foo")]

ElLenguajedeProgramacionRust

131FFI

#[link(name="foo",kind="bar")]

Enamboscasos,fooeselnombredelabibliotecanativaconlacualestamosenlazando,yenelsegundocasobareseltipodebibliotecanativaalacualestaenlazandoelcompilador.Actualmentehaytrestiposdebibliotecasnativasconocidos:

Dinamicas-#[link(name="readline")]Estaticas-#[link(name="my_build_dependency",kind="static")]Frameworks-#[link(name="CoreFoundation",kind="framework")]

NotaquelosframeworkssoloestándisponiblesenobjetivosOSX.

Losdiferentesvaloreskindtienencomoobjetodiferenciarcomolabibliotecanativaparticipaenelenlace.Desdelaperspectivadelenlace,elcompiladordeRustcreadostiposdeartefactos:parciales(rlib/staticlib)yfinales(dylib/binary).Lasdependenciasenbibliotecasnativasyframeworkssonpropagadasalafronteradeartefactofinal,mientrasquelasdependenciasestáticasnosonpropagadasdeltodo,debidoaquelasbibliotecasestáticasestánintegradasdirectamentedentrodelartefactosubsecuente.

Unospocosejemplosdecomoestemodelopuedeserusadoson:

Unadependenciadeconstrucciónnativa:AlgunasvecesalgúnC/C++depegamentoesnecesariocuandoseescribeuntipoespecificodecódigoRust,peroladistribucióndelcódigoenunformatodebibliotecaessimplementeunacarga.Enestecaso,elcódigoseraarchivadoenunlibfoo.ayluegoelcraterustdeclararaunadependenciavia#[link(name="foo",kind="static")].

Independientementedeltipodesalidaparaelcrate,labibliotecanativaestáticaseraincluidaenlasalida,significandoqueladistribucióndelabibliotecaestáticanativanoesnecesaria.

Unadependenciadinámicanormal:Bibliotecascomunesdelsistema(comoreadline)estándisponiblesenungrannumerodesistemas,yamenudounacopiaestáticadeesasbibliotecapuedenoexistir.CuandoestadependenciaesincluidaenuncrateRust,objetivosparciales(comorlibs)noenlazaranalabiblioteca,perocuandoelrlibesincluidoenunobjetivofinal(comounbinario),labibliotecaseraenlazada.

EnOSX,losframeworkssecomportanconlamismasemanticaqueunabibliotecadinámica.

BloquesUnsafe

ElLenguajedeProgramacionRust

132FFI

Algunasoperaciones,comodereferenciarpunterosplanosollamadasafuncionesquehansidomarcadascomoinsegurassolosonpermitidasdentrodebloquesunsafe.Losbloquesunsafeaíslanlainseguridadysonunapromesaalcompiladorquelainseguridadnosefiltrarahaciaelexteriordelbloque.

Lasfuncionesunsafe,porotrolado,hacenpublicidaddelainseguridadaelmundoexterior.Unafuncióninseguraseescribedelasiguientemanera:

unsafefnkaboom(ptr:*consti32)->i32{*ptr}

Estafunciónpuedeserinvocadasolodesdeunbloqueunsafeuotrafunciónunsafe.

AccediendoglobalesexternasAPIsforáneasalgunasvecesexportanunavariableglobalquepodríahaceralgocomollevarregistrodealgunestadoglobal.Conlafinalidaddeaccederdichasvariables,debesdeclararlasenbloquesexternconlapalabrareservadastatic:

##![feature(libc)]

externcratelibc;

#[link(name="readline")]

extern{

staticrl_readline_version:libc::c_int;

}

fnmain(){

println!("Tieneslaversion{}dereadline.",

rl_readline_versionasi32);

}

Alternativamente,podríasnecesitaralterarestadoglobalproporcionadoporunainterfazforánea.Parahaceresto,losestáticospuedenserdeclaradosconmutconlafinalidaddepodermutarlos.

ElLenguajedeProgramacionRust

133FFI

##![feature(libc)]

externcratelibc;

usestd::ffi::CString;

usestd::ptr;

#[link(name="readline")]

extern{

staticmutrl_prompt:*constlibc::c_char;

}

fnmain(){

letprompt=CString::new("[my-awesome-shell]$").unwrap();

unsafe{

rl_prompt=prompt.as_ptr();

println!("{:?}",rl_prompt);

rl_prompt=ptr::null();

}

}

Notaquetodalainteracciónconunstaticmutesinsegura,amboslecturayescritura.Lidiarconestadoglobalmutablerequieredeungrancuidado.

ConvencionesdellamadasforáneasLamayoríadeelcódigoforáneoexponeunABIC,yRustusapordefectolaconvencióndellamadasdelaplataformaalmomentodellamarfuncionesexternas.Algunasfuncionesforáneas,notablementeelAPIdeWindows,usanotraconvenióndellamadas.Rustproveeunaformadeinformaralcompiladoracercadecualconvencióndebeserusada:

##![feature(libc)]

externcratelibc;

#[cfg(all(target_os="win32",target_arch="x86"))]

#[link(name="kernel32")]

#[allow(non_snake_case)]

extern"stdcall"{

fnSetEnvironmentVariableA(n:*constu8,v:*constu8)->libc::c_int;

}

#fnmain(){}

Estoaplicaaelbloqueexterncompleto.LalistaderestriccionesABIsoportadasson:

stdcall

ElLenguajedeProgramacionRust

134FFI

aapcs

cdecl

fastcall

Rust

rust-intrinsic

system

C

win64

Lamayoríadelasabissonauto-explicativas,peroelabisystempuedeparecerunpocoraro.EstarestricciónseleccionacualquieraqueseaelABIapropiadoparainteroperarconlasbibliotecasobjetivo.Porejemplo,enwin32conunaarquitecturax86,significaqueelabiusadoserastdcall.Enx86_64,sinembargo,WindowsusalaconvencióndellamadasC,entoncesseusaC.Estosetraduceaqueennuestroejemploanterior,pudimoshaberhechousodeextern"system"{...}paradefinirunbloqueparatodoslossistemasWindows,nosololosx86.

InteroperabilidadconcódigoexternoRustgarantizaqueladistribucióndeunstructseacompatibleconlarepresentacióndelaplataformaenCsolosielatributo#[repr(C)]esaplicado.#[repr(C,packed)]puedeserusadoparadistribuirlosmiembrossinpadding.#[repr(C)]puedeseraplicadotambiénaunenum.

LosboxesRust(Box<T>)usanapuntadoresno-nulablescomohandlesqueapuntanaelobjetocontenido.Sinembargo,nodebensercreadosmanualmentedebidoquesonmanejadosporasignadoresinternos.Lareferenciaspuedenserasumidasdemaneraseguracomoapuntadoresno-nulablesdirectosaltipo.Sinembargo,romperelchequeodepréstamo(borrowing)olasreglasdemutabilidadnosegarantizaserseguro,esporelloqueseprefiereelusodeapuntadoresplanos(*)desernecesariodebidoaqueelcompiladornopuedeasumirmuchascosasacercadeellos.

Losvectoresylascadenasdecaracterescompartenlamismadistribuciónenmemoria,yutilidadesparainteractuarconAPIsCestándisponiblesenlosmódulosvecystr.Sinembargo,lascadenasdecaracteresnosonterminadasen\0.SinecesitasunacadenadecaracteresquetermineenNULparainteractuarconC,debesentonceshacerusodeeltipoCStringenelmodulostd::ffi.

LabibliotecaestándarincluyealiasdetipoydefinicionesdefuncionesparalabibliotecaestándardeCenelmodulolibc,yRustenlazapordefectoconlibcylibm.

ElLenguajedeProgramacionRust

135FFI

La"optimizaciondeapuntadornulable"Ciertostiposestándefinidosparanosernull.Estoincluyereferencias(&T,&mutT),boxes(Box<T>),yapuntadoresafunciones(extern"abi"fn()).CuandohacesinterfazconC,apuntadoresquepuedensernullsonusadosalgunasveces.Comouncasoespecial,unenumgenéricoquecontieneexactamentedosvariantes,delascualesunanocontienedataylaotracontieneunsolocampo,eselegibleparala"optimizaciondeapuntadornulable".Cuandodichaenumesinstanciadaconunodelostiposno-nulables,esrepresentadacomounsoloapuntador,ylavariantesindataesrepresentadacomoelapuntadornull.EntoncesOption<extern"C"fn(c_int)->c_int>escomounorepresentaunapuntadorafunciónnullableusandoelABIdeC.

LlamandocodingRustdesdeCPodríasquerercompilarcódigoRustdemodoquepermitaserllamadodesdeC.Estoesfácil,perorequiereciertascosas:

#[no_mangle]

pubexternfnhola_rust()->*constu8{

"Hola,mundo!\0".as_ptr()

}

#fnmain(){}

ElexternhacequeestafunciónseadhieraalaconvencióndellamadasdeC,talycomosediscuteen"Convencionesdellamadasforáneas".Elatributono_mangledesactivaelestropeo(mangling)denombresdeRust,demaneratalqueseamasfacildeenlazar.

FFIypánicosEsimportanteestarconscientedelospanic!oscuandosetrabajaconFFI.Unpanic!enallimitedeFFIescomportamientoindefinido.Siestasescribiendocódigoquepuedahacerpánico,debesejecutarloenotrohilo,deestaformaelpániconosepropagarahastaC:

ElLenguajedeProgramacionRust

136FFI

usestd::thread;

#[no_mangle]

pubexternfnoh_no()->i32{

leth=thread::spawn(||{

panic!("Oops!");

});

matchh.join(){

Ok(_)=>1,

Err(_)=>0,

}

}

#fnmain(){}

ElLenguajedeProgramacionRust

137FFI

%BorrowyAsRef

LosttraitsBorrowandAsRefsonmuysimilares,perodiferentes.Heaquíunbreverepasoacercadeloquesignificandichostraits:

BorrowEltraitBorrowseusacuandoestamosescribiendounestructuradedatos,ydeseamosusaruntipoownedountipoborrowedcomosinónimoporalgunarazón.

Porejemplo,HashMapposeeunmetodogetqueusaBorrow:

fnget(&self,k:&Q)->Option<&V>

whereK:Borrow“,

Q:Hash+Eq

Estafirmaescomplicada.ElparametroKesenloqueestamosinteresadosahora.SerefiereaunparametrodelHashMapensimismo:

structHashMap{

ElparametroKeseltipodelaclavequeusaelHashMap.Alverdenuevolafirmadeget(),solopodemosusarget()cuandolaclaveimplementaBorrow<Q>.Deestamanera,podemoscrearunHashMapqueuseclavesStringperoalmomentodebuscaruse&strs:

usestd::collections::HashMap;

letmutmap=HashMap::new();

map.insert("Foo".to_string(),42);

assert_eq!(map.get("Foo"),Some(&42));

EstoesdebidoaquelabibliotecaestándarposeeunaimplBorrow<str>forString.

Paralamayoríadelostipos,cuandodeseestomaruntipoyaseaownedoborrowed,un&Tessuficiente.PerounareaenlacualBorrowesefectivoescuandohaymasdeuntipodevalorborrowed.Estoesespecialmenteciertoenreferenciasyslices:puedestenerambosun&Tun&mutT.SiqueremosaceptarambostiposBorrowesloquenecesitamos:

ElLenguajedeProgramacionRust

138BorrowyAsRef

usestd::borrow::Borrow;

usestd::fmt::Display;

fnfoo<T:Borrow<i32>+Display>(a:T){

println!("aesunborrowed:{}",a);

}

letmuti=5;

foo(&i);

foo(&muti);

Thiswillprintoutaesunborrowed:5dosveces.

AsRefEltraitAsRefesuntraitdeconversion.Esusadoparaconvertiralgúnvaloraunareferenciaencódigogenerico.Comoesto:

lets="Hola".to_string();

fnfoo<T:AsRef<str>>(s:T){

letslice=s.as_ref();

}

Cualdeberíausar?Podemosvercomoambossonunaespeciedelomismo:amboslidianconlasversionesownedyborroweddealgúntipo.Sinembargo,sondiferentes.

EscogeBorrowcuandoquierasabstraerteporencimadediferentestiposdeborrowing,ocuandoestasconstruyendounaestructuradedatosquetratalosvaloresownedyborroweddeformasequivalentes,comoelhashingylacomparación.

UsaAsRefcuandodeseesconvertiralgodirectamenteenunareferencia,yestésescribiendocódigogenérico.

ElLenguajedeProgramacionRust

139BorrowyAsRef

%CanalesdeDistribución

ElproyectoRustusaunconceptodenominado‘canalesdedistribución’paraadministrarlosreleases.

EsimportanteentenderesteprocesoparaasípoderdecidircualversiondeRustdebeusartuproyecto.

VisióngeneralHaytrescanalesparalosreleasesdeRust:

Nocturno(nightly)BetaEstable(stable)

Losreleasesnocturnossoncreadosunavezaldía.Cadaseissemanas,elultimoreleasenocturnoespromovidoa'Beta'.Enestepunto,solorecibiráparchesquearreglenerroresserios.Seissemanasdespuéselbetaespromovidoa'Estable',yseconvierteenelnuevoreleasede1.x.

Esteprocesoocurreenparalelo.Cadaseissemanas,enelmismodíaelnocturnosubeabeta,elbetaespromovidoaestable.Cuando1.xesliberado,almismotiempo,1.(x+1)-betaesliberado,yelnocturnosevuelvelaprimeraversionde1.(x+2)-nightly.

EscogiendounaversiónGeneralmentehablando,amenosquetengasunarazónespecifica,deberíasusarelcanaldedistribuciónestable.Esosreleasestienencomoobjetivounaaudienciageneral.

Sinembargo,dependiendodetuinterésenRust,podríasescogerelnocturno.Elbalanceeselsiguiente:enelcanalnocturno,puedeshacerusodenuevascaracterísticasdeRust.Sinembargo,lascaracterísticasinestablesestánsujetasalcambio,yesporelloquecualquiernocturnotienelaposibilidadderompertucódigo.Siusaselreleaseestable,notienesaccesoacaracterísticasexperimentales,peroelsiguientereleasedeRustnocausaraproblemassignificativosacausadecambios.

AyudandoaelecosistemaatravesdeCI

ElLenguajedeProgramacionRust

140CanalesdeDistribución

Quehayacercadebeta?NosotrosrecomendamosatodoslosusuariosRustqueusanelcanaldedistribuciónestableaquetambiénpruebenensussistemasdeintegracióncontinuaconelcanalbeta.Estoayudaraaadvertiralequipoencasodequeexistaunaregresiónaccidental.

Adicionalmente,probarcontraelnocturnopuededetectarregresionesmuchomastemprano,esporelloquesinoteimportateneruntercerbuild,apreciamosquepruebescontratodosloscanales.

ElLenguajedeProgramacionRust

141CanalesdeDistribución

%SintaxisySemántica

EstaseccióndivideaRustenpequeñospedazos,unoparacadaconcepto.

SideseasaprenderRustdeabajohaciaarriba,leerestasecciónenordenesunamuybuenaformadehacerlo.

Estasseccionesformanunareferenciaparacadaconcepto,siestasleyendootrotutorialyencuentrasalgoconfuso,puedesencontrarloexplicadoenalgúnlugardeestasección.

ElLenguajedeProgramacionRust

142SintaxisySemantica

%EnlacesaVariable

Virtualmentecualquierprogramano-'HolaMundo’usaenlacesavariables.Dichosenlacesavariableslucenasí:

fnmain(){

letx=5;

}

Colocarfnmain(){encadaejemploesunpocotedioso,asíqueenelfuturoloomitiremos.Siestassiguiendopasoapaso,aseguratedeeditartufunciónmain(),enlugardedejarlaporfuera.Deotromodoobtendrásunerror.

Enmuchoslenguajes,estoesllamadounavariable,perolosenlacesavariabledeRusttienenunpardetrucosbajolamanga.Porejemploelladoizquierdodeunaexpresiónletesun‘patron’,nounsimplenombredevariable.Estosetraduceenquepodemoshacercosascomo:

let(x,y)=(1,2);

Despuésdequeestaexpresiónesevaluada,xserauno,yyserados.Lospatronessonrealmentepoderosos,ytienensupropiasecciónenellibro.Nonecesitamosesasfacilidadesporahora,solomantengamosestoennuestrasmentesmientrasavanzamos.

Rustesunlenguajeestáticamentetipificado,loquesignificaqueespecificamosnuestrostiposporadelantado,yestossonchequeadosentiempodecompilación.Entonces,porquenuestroprimerejemplocompila?Bueno,Rusttieneestacosallamada‘inferenciadetipos’.Sipuededeterminareltipodealgo,Rustnorequierequeescribaseltipo.

Podemosagregareltiposilodeseamos.Lostiposvienendespuésdedospuntos(:):

letx:i32=5;

Sitepidieraleerestoenvozaltaalrestodelaclase,dirías“xesunenlaceconeltipoi32yelvalorcinco.”

Enestecasodecidimosrepresentarxcomounenteroconsignode32bits.Rustposeemuchostiposdeenterosprimitivosdiferentes.Estoscomienzanconiparalosenterosconsignoyconuparalosenterossinsigno.Lostamañosposiblesparaenterosson8,16,32,y64bits.

Enejemplosfuturos,podríamosanotareltipoenuncomentario.Elejemploluciríaasí:

ElLenguajedeProgramacionRust

143EnlacesaVariables

fnmain(){

letx=5;//x:i32

}

Notalassimilitudesentrelaanotaciónylasintaxisqueusasconlet.IncluirestaclasedecomentariosnoesidiomáticoenRust,perolosincluiremosocasionalmenteparaayudarteaentendercualessonlostiposqueRustinfiere.

Pordefecto,losenlacessoninmutables.Estecódigonocompilara:

letx=5;

x=10;

Daracomoresultadoelsiguienteerror:

error:re-assignmentofimmutablevariable`x`

x=10;

^~~~~~~

Sideseasqueunenlaceavariableseamutable,puedeshacerusodemut:

letmutx=5;//mutx:i32

x=10;

Nohayunarazónúnicaporlacuallosenlacesavariablesoninmutablespordefecto,peropodemospensaracercadeelloatravésdeunodelosfocosprincipalesdeRust:seguridad.Siolvidasdecirmut.elcompiladorlonotará,yteharásaberquehasmutadoalgoquenoteniasintencióndemutar.Silosenlacesavariablesfuesenmutablespordefecto,elcompiladornoseriacapazdedecirteesto.Siteniaslaintencióndemutaralgo,entonceslasoluciónesmuyfácil:agregarmut.

Hayotrasbuenasrazonesparaevitarestadomutablesiemprequeseaposible,peroestanfueradelalcancedeestaguisa.Engeneral,puedesfrecuentementeevitarmutaciónexplicita,yestoespreferibleenRust.Dichoesto,algunasveces,lamutaciónesjustoloquenecesitas,esporelloquenoestaprohibida.

Volvamosalosenlacesavariables.LosenlacesavariableenRustposeenunaspectomasquedifieredeotroslenguajes:serequiereesteninicializadosaunvalorantesquepuedasusarlos.

Pongamosestoaprueba.Cambiatuarchivosrc/main.rsparaqueluzcaasí:

ElLenguajedeProgramacionRust

144EnlacesaVariables

fnmain(){

letx:i32;

println!("Hola,mundo!");

}

Puedesusarcargobuildenlalineadecomandoparacompilarlo.Obtendrásunaadvertenciaperoaunasíimprimirá"Hola,mundo!":

Compilinghola_mundov0.0.1(file:///home/tu/proyectos/hola_mundo)

src/main.rs:2:9:2:10warning:unusedvariable:`x`,#[warn(unused_variable)]

onbydefault

src/main.rs:2letx:i32;

^

Rustnosadviertequenuncahacemosusodelenlaceavariable,perodebidoaquenuncalausamos,nohaypeligro,nohayfalta.Sinembargo,lascosascambiansiefectivamenteintentamosusarx.Hagamoseso.Cambiatuprogramaparaqueluzcadelasiguientemanera:

fnmain(){

letx:i32;

println!("Elvalordexes:{}",x);

}

Intentacompilarlo.Obtendrásunerror:

$cargobuild

Compilinghola_mundov0.0.1(file:///home/tu/proyectos/hola_mundo)

src/main.rs:4:39:4:40error:useofpossiblyuninitializedvariable:`x`

src/main.rs:4println!("Elvalordexes:{}",x);

^

note:inexpansionofformat_args!

<stdmacros>:2:23:2:77note:expansionsite

<stdmacros>:1:1:3:2note:inexpansionofprintln!

src/main.rs:4:5:4:42note:expansionsite

error:abortingduetopreviouserror

Couldnotcompile`hello_world`.

Rustnotepermitiráusarunvalorquenohayasidoinicializadopreviamente.Acontinuaciónhablemosdeloquelehemosagregadoaprintln!.

ElLenguajedeProgramacionRust

145EnlacesaVariables

Siincluyesunpardellaves({},algunaspersonaslosllamanbigotes/moustaches...)enlacadenadecaracteresaimprimir,Rustlointerpretaracomounapeticiónparainterpolaralgunaclasedevalor.Lainterpolaciónencadenasdecaracteresesunterminoencienciasdelacomputaciónquesignifica"colocaestodentrodelacadenadecaracteres".Agregamosunacoma,yluegox,paraindicarquequeremosqueesteseaelvalorinterpolado.Lacomaesusadaparasepararlosargumentosquepasamosalasfuncionesylosmacros,enelcasodepasarmasdeuno.

Cuandousaslasllaves,Rustintentarámostrarelvalordeunaformaquetengasentidodespuésdechequearsutipo.Sideseamosespecificarunformatomasdetallado,existenunamplionumerodeopcionesdisponible.Porahora,nosapegaremosalcomportamientopordefecto:losnúmerosenterosnosonmuycomplicadosdeimprimir.

ElLenguajedeProgramacionRust

146EnlacesaVariables

%Funciones

TodoprogramaRustposeealmenosunafunción,lafunciónmain:

fnmain(){

}

Estaesladeclaracióndefunciónmassimpleposible.Comohemosmencionadoanteriormente,fndice‘estoesunafunción’,seguidodelnombre,paréntesisdebidoaqueestafunciónnorecibeningúnargumento,yluegollavesparaindicarelcuerpodelafunción.Heaquíunafunciónllamadafoo:

fnfoo(){

}

Entonces,quehayacercaderecibirargumentos?Acontinuaciónunafunciónqueimprimeunnumero:

fnimprimir_numero(x:i32){

println!("xes:{}",x);

}

Acáunprogramacompletoquehaceusodeimprimir_numero:

fnmain(){

imprimir_numero(5);

}

fnimprimir_numero(x:i32){

println!("xes:{}",x);

}

Comopodrásver,losargumentosdefuncióntrabajandemaneramuysimilaralasdeclaracioneslet:agregasuntipoaelnombredelargumento,despuésdedospuntos.

Heaquíunprogramacompletoquesumadosnúmerosyluegolosimprime:

fnmain(){

imprimir_suma(5,6);

}

fnimprimir_suma(x:i32,y:i32){

println!("lasumaes:{}",x+y);

}

ElLenguajedeProgramacionRust

147Funciones

Losargumentossonseparadosporunacoma,enamboscasos,cuandollamasalafunciónasícomocuandoladeclaras.

Adiferenciadelet,debesdeclararlostiposdelosargumentos.Losiguiente,nocompilara:

fnimprimir_suma(x,y){

println!("sumaes:{}",x+y);

}

Obtendríaselsiguienteerror:

expectedoneof`!`,`:`,or`@`,found`)`

fnimprimir_suma(x,y){

Estoesunadeliberadadecisióndediseño.Aunquelainferenciadelprogramacompletoespossible,loslenguajesquelaposeen,comoHaskell,amenudosugierenquedocumentartustiposdemaneraexplicitaesunabuenapractica.Nosotroscoincidimosenqueobligaralasfuncionesadeclararlostiposalmismotiempopermitiendoinferenciadentrodelafunciónesunmaravillosopuntomedioentreinferenciacompletaylaausenciadeinferencia.

Quehayacercaderetornarunvalor?Acontinuaciónunafunciónquesumaunoaunentero:

fnsuma_uno(x:i32)->i32{

x+1

}

LafuncionesenRustretornanexactamenteunvalor,yeltipoesdeclaradodespuésdeuna‘flecha’,queesunguión(-)seguidoporunsignomayor-que(>).Laultimalineadeunafuncióndeterminaloqueestaretorna.Notaraslaausenciadeunpuntoycomaaquí.Deagregarlo:

fnsuma_uno(x:i32)->i32{

x+1;

}

Obtendríamosunerror:

ElLenguajedeProgramacionRust

148Funciones

error:notallcontrolpathsreturnavalue

fnsuma_uno(x:i32)->i32{

x+1;

}

help:considerremovingthissemicolon:

x+1;

^

LoanteriorreveladoscosasinteresantesacercadeRust:esunlenguajebasadoenexpresiones,ylospuntosycomasondiferentesalosdeotroslenguajesbasadosen‘llavesypuntosycoma’.Estasdoscosasestánrelacionadas.

Expresionesvs.SentenciasRustenunlenguajeprincipalmentebasadoenexpresiones.Existensolodostiposdesentencias,todolodemásesunaexpresión.

Entonces,cualesladiferencia?Lasexpresionesretornanunvalor,ylassentenciasno.Esporelloqueterminamosconelerror‘notallcontrolpathsreturnavalue’aquí:lasentenciax+1;noretornaningúnvalor.HaydostiposdesentenciasenRust:‘sentenciasdedeclaración’and‘sentenciasdeexpresión’.Todolodemásesunaexpresión.Hablemosprimerodelassentenciasdedeclaración.

Enalgunoslenguajes,losenlacesavariablepuedenserescritoscomoexpresiones,nosolosentencias.ComoenRuby:

x=y=5

EnRust,sinembargo,elusodeletparaintroducirunenlaceavariablenoesunaexpresión.Losiguienteproduciráunerrorentiempodecompilación:

letx=(lety=5);//expectedidentifier,foundkeyword`let`

Elcompiladornosdicequeestabaesperandoelcomienzodeunaexpresión,yunletpuedesersolounasentencia,nounaexpresión.

Notaquelaasignaciónaunavariablequehasidopreviamenteasignada(e.j.y=5)esunaexpresión,aunasísuvalornoesparticularmenteutil.Adiferenciadeotroslenguajesenlosqueunaasignaciónesevaluadaaelvalorasignado(e.j.5enejemploanterior),enRustelvalordeunaasignaciónesunatuplavacía()debidoaqueelvalorasignadopuedetenerunsolodueñoycualquierotrovalorderetornoseriasorpresivo:

ElLenguajedeProgramacionRust

149Funciones

letmuty=5;

letx=(y=6);//xposeeelvalor`()`,no`6`

ElsegundotipodesentenciaenRusteslasentenciadeexpresión.Supropósitoesconvertircualquierexpresiónenunasentencia.Entérminosprácticos,lagramáticadeRustesperaqueunasentenciaseaseguidapormassentencias.Estosignificaquepuedesusarpuntosycomaparasepararunaexpresióndeotra.EstocausaqueRustluzcamuysimilaraesoslenguajesenloscualesserequiereunpuntoycomaalfinaldecadalinea,dehecho,observaraspuntosycomaalfinaldecasitodaslaslineasdeRustqueveras.

Entonces,Cualeslaexcepciónquenoshacedecir"casi"?Yalohasvistoenelcódigoanterior:

fnsuma_uno(x:i32)->i32{

x+1

}

Nuestrafunciónclamaretornaruni32,peroconunpuntoycoma,retornaría().Rustdeterminaqueestonoesprobablementeloquequeremos,ysugierelaremocióndelpuntoycomaenelerrorquevimosanteriormente.

RetornostempranosPeroquehayacercadelosretornostempranos?Rustproporcionaunapalabrareservadaparaello,return:

fnfoo(x:i32)->i32{

returnx;

//nuncaalcanzaremosestecódigo!

x+1

}

Usarunreturncomolaultimalineadeunafunciónesvalido,peroesconsideradopobreestilo:

fnfoo(x:i32)->i32{

returnx+1;

}

ElLenguajedeProgramacionRust

150Funciones

Ladefiniciónpreviasinelreturnpuedelucirunpocoextrañasinuncahastrabajadoconunlenguajebasadoenexpresiones,peroestasevuelveintuitivaconeltiempo.

FuncionesdivergentesRusttieneunasintaxisespecialparalas‘funcionesdivergentes’,quesonfuncionesquenoretornan:

fndiverge()->!{

panic!("Estafunciónnuncaretorna!");

}

panic!esunamacro,similaralaprintln!()queyahemosvisto.Adiferenciadeprintln!(),panic!()causaqueelhilodeejecuciónactualterminedemaneraabruptamostrandoelmensajeproporcionado.

Debidoaqueestafuncióncausaraunasalidaabrupta,estanuncaretornara,esporelloqueposeeeltipo‘!’,queselee‘diverge’.Unafuncióndivergentepuedeserusadacomocualquiertipo:

#fndiverge()->!{

#panic!("Estafunciónnuncaretorna!");

#}

letx:i32=diverge();

letx:String=diverge();

ApuntadoresafunciónTambiénpodemoscrearenlacesavariablesqueapuntenafunciones:

letf:fn(i32)->i32;

fesunenlaceavariablequeapuntaaunafunciónquetomauni32comoargumentoyretornauni32.Porejemplo:

ElLenguajedeProgramacionRust

151Funciones

fnmas_uno(i:i32)->i32{

i+1

}

//sininferenciadetipos

letf:fn(i32)->i32=mas_uno;

//inferenciadetipos

letf=mas_uno;

Podemosentonceshacerusodefparallamaralafunción:

#fnmas_uno(i:i32)->i32{i+1}

#letf=mas_uno;

letseis=f(5);

ElLenguajedeProgramacionRust

152Funciones

%TiposPrimitivos

Rustposeeunconjuntodetiposquesonconsiderados‘primitivos’.Estosignificaqueestánintegradosenellenguaje.Rustestaestructuradodetalmaneraquelabibliotecaestándartambiénproveeunanumerodetiposútilesbasadosenlosprimitivos,peroestossonlosmasprimitivos.

BooleanosRustposeeuntipobooleanointegrado,denominadobool.Tienedosposiblesvalorestrueyfalse:

letx=true;

lety:bool=false;

Unusocomúndelosbooleanosesencondicionalesif.

Puedesencontrarmasdocumentaciónparalosbooleanosenladocumentacióndelabibliotecaestándar(ingles).

char

EltipocharrepresentaununicovalorescalarUnicode.Puedescrearcharsconcomillassimples:(')

letx='x';

letdos_corazones='';

Adiferenciadeotroslenguajes,estosignificaquecharenRustnoesunsolobyte,sinocuatro.

Puedesencontrarmasdocumentaciónparaloscharsenladocumentacióndelabibliotecaestándar(ingles).

TiposnuméricosRustposeeunavariedaddetiposnuméricosenunaspocascategorías:consignoysinsigno,fijosyvariables,depuntoflotanteyenteros.

ElLenguajedeProgramacionRust

153TiposPrimitivos

Dichostiposconsistendedospartes:lacategoría,yeltamaño.Porejemplo,u16esuntiposinsignoconuntamañodedieciséisbits.Masbitstepermitenalmacenarnúmerosmasgrandes.

Siunliteraldenumeronoposeenadaquecauselainferenciadesutipo,seusantipospordefecto:

letx=42;//xtieneeltipoi32

lety=1.0;//ytieneeltipof64

Heaquíunalistadelosdiferentestiposnuméricos,conenlacesasudocumentaciónenlabibliotecaestándar(ingles):

i8i16i32i64u8u16u32u64isizeusizef32f64

Veamoscadaunadelasdiferentescategorías.

ConsignoysinsignoLostiposdeenterosvienenendosvariedades:consignoysinsigno.Paraentenderladiferencia,consideremosunnumeroconcuatrobitsdetamaño.Unnumerodecuatrobitsconsignotepermitiríaalmacenarlosnúmerosdesde-8a+7.Losnúmerosconsignousanla“representacióndelcomplementoados”.Unnumerodecuatrosbitssinsigno,debidoaquenonecesitaguardarlosvaloresnegativos,puedealmacenarvaloresdesde0hasta+15.

Lostipossinsignousanunauparasucategoria,ylostiposconsignousani.Laiesdeinteger(entero).Entonces,u8esunnumerodeochobitssinsigno,yuni8esunnumerodeochobitsconsigno.

ElLenguajedeProgramacionRust

154TiposPrimitivos

TiposdetamañofijoLostiposdetamañofijoposeenunnumeroespecificodebitsensurepresentación.Lostamañosvalidosson8,16,32,and64.Entoncesu32esunenterosinsignode32bits,ei64esunenteroconsignode64bits.

TiposdetamañovariableRusttambiénproveetiposparaloscualeseltamañodependedeltamañodelapuntadorenlamaquinasubyacente.Dichostiposposeenlacategoría‘size’,yvienenenvariantesconysinsigno.Estoresultaendostiposisizeyusize.

TiposdepuntoflotanteRusttambiénposeedostiposdepuntoflotante:f32yf64.EstoscorrespondenalosnúmerosIEEE-754desimpleydobleprecision.

ArreglosComomuchoslenguajesdeprogramación,Rustposeetiposlistapararepresentarunasecuenciadecosas.Elmasbásicoeselarreglo,unalistadeelementosdelmismotipoydetamañofijo.Pordefectolosarreglossoninmutables.

leta=[1,2,3];//a:[i32;3]

letmutm=[1,2,3];//m:[i32;3]

Losarreglostieneneltipo[T;N].HablaremosdelanotaciónTenlaseccióndegenéricos.LaNesunaconstanteentiempodecompilación,paralalongituddelarreglo.

Hayunatajoparalainicializacióndecadaunodeloselementosdelarregloaelmismovalor.Enesteejemplo,cadaelementodeaserainicializadoa0:

leta=[0;20];//a:[i32;20]

Puedesobtenerelnumerodeelementosdelarregloacona.len()

ElLenguajedeProgramacionRust

155TiposPrimitivos

leta=[1,2,3];

println!("atiene{}elementos",a.len());

Puedesaccederunelementodelarregloenparticularconlanotacióndesubindices:

letnombres=["Graydon","Brian","Niko"];//names:[&str;3]

println!("Elsegundonombrees:{}",nombres[1]);

Lossubindicescomienzanencero,comoenlamayoríadeloslenguajesdeprogramación,entonceselprimernombreesnombres[0]yessegundoesnombres[1].Elejemploanteriorimprime:Elsegundonombrees:Brian.Siintentasusarunsubíndicequenoestaenelarreglo,obtendrásunerror:elchequeodeloslimitesenelaccesoalarregloserealizaentiempodeejecución.Elaccesoerrantecomoeseeslafuentedemuchosbugsenloslenguajesdeprogramacióndesistemas.

Puedesencontrarmasdocumentaciónparalosarraysenladocumentacióndelabibliotecaestándar(ingles).

SlicesUnsliceesunareferenciaa(ouna“vista”dentrode)otraestructuradedatos.Losslicessonútilesparapermitiraccesoseguroyeficienteaunaporcióndeunarreglosininvolucrarelcopiado.Porejemplo,podríasquererhacerreferenciaaunasolalineadeunaarchivoquehasidopreviamenteleídoenmemoria.Pornaturaleza,unslicenoescreadodirectamente,estossoncreadosparaunavariablequeyaexiste.Losslicesposeenunalongitud,puedensermutablesoinmutables,yenmuchasformassecomportancomolosarreglos:

leta=[0,1,2,3,4];

letmiddle=&a[1..4];//Unslicedea:sololoselementos1,2,y3

letcomplete=&a[..];//Unsliceconteniendotodosloselementosdea.

Losslicesposeeneltipo&[T].HablaremosacercadeTcuandocubramoslosgenericos.

Puedesencontrarmasdocumentaciónparalosslicesenladocumentacióndelabibliotecaestándar(ingles)

str

ElLenguajedeProgramacionRust

156TiposPrimitivos

ElstrdeRusteseltipodecadenadecaracteresmasprimitivo.Comountiposintamaño,ynoesmuyutilensimismo,perosevuelvemuyutilcuandoespuestodetrásdeunareferencia,como&str.Esporelloquelodejaremoshastaaquí.

Puedesencontrarmasdocumentaciónparastrenladocumentacióndelabibliotecaestándar(ingles)

TuplasUnatuplaesunalistaordenadadetamañofijo.Comoesto:

letx=(1,"hola");

Losparéntesisycomasformanestatupladelongituddos.Heaquíelmismocódigoperoconanotacionesdetipo:

letx:(i32,&str)=(1,"hola");

Comopuedesver,eltipodeunatuplaesjustocomolatupla,peroconcadaposiciónteniendoeltipoenlugardelvalor.Loslectorescuidadosostambiénnotaranquelastuplassonheterogéneas:tenemosuni32ya&strenestatupla.Enloslenguajesdeprogramacióndesistemas,lascadenasdecaracteressonunpocomascomplejasqueenotroslenguajes.Porahorasololee&strcomounslicedecadenadecaracteres.Aprenderemosmasdentrodepoco.

Puedesasignarunatuplaaotra,siestastienenlosmismotiposcontenidosylamismaaridad.Latuplesposeenlamismaaridadcuandoestastienenelmismotamaño.

letmutx=(1,2);//x:(i32,i32)

lety=(2,3);//y:(i32,i32)

x=y;

Puedesaccederaloscamposdeunatuplaatravésdeunletcondestructuracion.Heaquíunejemplo:

let(x,y,z)=(1,2,3);

println!("xes{}",x);

ElLenguajedeProgramacionRust

157TiposPrimitivos

Recuerdasanteriormentecuandodijequeelladoizquierdodeunasentencialeteramaspoderosoquelasimpleasignacióndeunbinding?Henosaquí.Podemoscolocarunpatronenelladoizquierdodeunlet,ysiesteconcuerdaconelladoderecho,podemosasignarmultiplesbindingsavariabledeunasolavez.Enestecaso,ellet“destructura”o“parte”latupla,yasignalaspartesalostresbindingsavariable.

Estepatronesmuypoderoso,yloveremosrepetidoconfrecuenciaenelfuturo.

Puedeseliminarlaambigüedadentreunatupladeunsoloelementoyunvalorencerradoenparéntesisusandounacoma:

(0,);//tupladeunsoloelemento

(0);//ceroencerradoenparentesis

IndexadoentuplasPuedestambiénaccederloscamposdeunatuplaconlasintaxisdeindexado:

lettupla=(1,2,3);

letx=tupla.0;

lety=tupla.1;

letz=tupla.2;

println!("xes{}",x);

Aligualqueelindexadoenarreglos,estecomienzaencero,peroadiferenciadeelindexadoenarreglos,seusan.,enlugarde[]s.

Puedesencontrarmasdocumentaciónparatuplasenladocumentacióndelabibliotecaestándar(ingles)

FuncionesLasfuncionestambiéntienenuntipo!Estaslucenasí:

fnfoo(x:i32)->i32{x}

letx:fn(i32)->i32=foo;

Enestecaso,xesun‘apuntador’aunafunciónquerecibeuni32yretornauni32.

ElLenguajedeProgramacionRust

158TiposPrimitivos

%Comentarios

Ahoraquehemosvistosalgunasfunciones,esbuenaideaaprendersobreloscomentarios.Loscomentariossonnotasquedejasaotrosprogramadoresconlafinalidaddeexplicaralgúnaspectodetucódigo.Elcompiladormayormente,losignora.

Rustposeedostiposdecomentariosquetedebeninteresar:comentariosdelineaycomentariosdededocumentación

//Loscomentariosdelineasoncualquiercosadespuésde‘//’yseextiendenhastaelfinaldelalinea

letx=5;//esteestambiénuncomentariodelinea

//Sitienesunalargaexplicaciónacercadealgo,puedescolocarcomentarios

//delineaunosjuntos.Ponunespacioentretus//ytucomentarioconel

//findehacerlosmaslegibles.

Elotrotipodecomentarioesuncomentariodedocumentación(ocomentariodoc).Loscomentariosdocusan///enlugarde//,ysoportannotaciónMarkdownensuinterior:

///Sumaunoalnumeroproporcionado

///

///#Examples

///

///

///letcinco=5;//////assert_eq!(6,suma_uno(5));///#fnsuma_uno(x:i32)->i32{///#x+1///#}///```fnsuma_uno(x:i32)->i32{x+1}

Existeotroestilodecomentarios,`//!`,conlafinalidaddecomentarlositemscontenedores(e.jcrates,modulosofunciones),enlugardelositemsquelossiguen.Sonusadoscomúnmenteenlaraizdeuncrate(lib.rb)oenlaraizdeunmodulo(mod.rs):

//!#LaBibliotecaEstándardeRust//!//!LaBibliotecaEstándardeRustproporcionalafuncionalidad//!entiempodeejecuciónesencialparalaconstruccióndesoftware//!Rustportable.```

Cuandoseescribencomentariosdoc,proporcionaralgunosejemplosdeusoesmuy,muyutil.Notarasquehemoshechousodeunamacro:assert_eq!.Estacomparadosvalores,yhacepanic!osiestosnosoniguales.Esmuyutilenladocumentación.Tambiénexisteotramacro,assert!,lacualhacepanic!osielvalorproporcionadoesfalse.

PuedesusarlaherramientarustdocparagenerardocumentaciónenHTMLapartirdedichoscomentarios,asícomoejecutarelcódigodelosejemploscomopruebas!

ElLenguajedeProgramacionRust

159Comentarios

%if

ElenfoquedeRustconrelaciónaifnoesparticularmentecomplejo,peroesmassimilaraelifqueencontrarasenlenguajescontipificadodinámicoquealdeloslenguajesdesistemastradicionales.Hablemosunpocoacercadeeste,paraestarsegurosdequeentiendastodoslosmatices.

ifesunaformaespecificadeaunconceptomasgeneral,el‘branch’(rama).Elnombreprovienedeunaramaenunárbol:esunpuntodedecisión,enelcualdependiendodeunaopción,multiplescaminospuedensertomados.

Enelcasodeif,hayunasolaelecciónqueconduceadoscaminos:

letx=5;

ifx==5{

println!("xescinco!");

}

Dehabercambiadoelvalordexaalgodiferente,estalineanohubiesesidoimpresa.Parasermasespecifico,silaexpresióndespuésdelifesevaluadaatrue,entonceselbloquedecódigoesejecutado.Siesfalse,dichobloquenoseinvoca.

Sideseasquealgoocurraenelcasodefalse,debeshacerusodeunelse:

letx=5;

ifx==5{

println!("xescinco!");

}else{

println!("xnoescinco:(");

}

Dehabermasdeuncaso,usaunelseif:

letx=5;

ifx==5{

println!("xescinco!");

}elseifx==6{

println!("xesseis!");

}else{

println!("xnoesnicinconiseis:(");

}

Todoestoesbienestándar.Sinembargo,tambiénpuedeshaceresto:

ElLenguajedeProgramacionRust

160if

letx=5;

lety=ifx==5{

10

}else{

15

};//y:i32

Locualpodemos(yprobablementedeberiamos)escribirasí:

letx=5;

lety=ifx==5{10}else{15};//y:i32

Estofuncionaporqueifesunaexpresión.Elvalordelaexpresióneselvalordelaultimaexpresiónenelbloquequehayasidoseleccionado.Unifsinunelsesiempreresultaen()comovalor.

ElLenguajedeProgramacionRust

161if

%Ciclos

Rustactualmenteproveetresenfoquespararealizaractividaditerativa.loop,whileyfor.Cadaunodedichosenfoquestienesuspropiosusos.

loopElcicloinfinitoloopeselciclomassimpledisponibleenRust.Atravesdelusodelapalabrareservadaloop,Rustproporcionaunaformadeiterarindefinidamentehastaquealgunasentenciadeterminaciónseaalcanzada.ElloopinfinitodeRustluceasí:

loop{

println!("Iteraporsiempre!");

}

whileRusttambiéntieneunciclowhile.Lucedeestamanera:

letmutx=5;//mutx:i32

letmutcompletado=false;//mutcompletado:bool

while!completado{

x+=x-3;

println!("{}",x);

ifx%5==0{

completado=true;

}

}

Loscicloswhilesonlaeleccióncorrectacuandonoestasseguroacercadecuantasvecesnecesitasiterar.

Denecesitaruncicloinfinito,podríassentirtetentadoaescribiralgocomoesto:

whiletrue{

Sinembargo,loopesporlejos,elmejorparaestecaso:

loop{

ElLenguajedeProgramacionRust

162Ciclos

ElanálisisdeflujodecontroldeRusttrataestaconstruccióndemaneradiferentequeaunwhiletrue,debidoaquesabemosqueiteraremosporsiempre.Engeneral,mientrasmasinformaciónleproporcionemosalcompilador,estepodríadesempeñarsemejorenrelaciónalaseguridadygeneracióndecódigo,esporelloquesiempredeberíaspreferirloopcuandotengasplaneadoiterardemaneraindefinida.

forElcicloforesusadoparaiterarunnumeroparticulardeveces.LosciclosfordeRust,sinembargo,trabajandemaneradiferentealosdeotroslenguajesdeprogramacióndesistemas.ElfordeRustnolucecomoestecicloforal“estiloC”

for(x=0;x<10;x++){

printf("%d\n",x);

}

Ensulugar,elciclofordeRustluceasí:

forxin0..10{

println!("{}",x);//x:i32

}

Entérminosligeramentemasabstractos:

forvarinexpresion{

código

}

Laexpresiónesuniterador.Eliteradorretornaunaseriedeelementos.Cadaelementoesunaiteracióndelciclo.Esevaloresasuvezasignadoaelnombrevar,elcualesvalidoenelcuerpodelciclo.Unavezqueelciclohaterminado,elsiguientevaloresobtenidodeliterador,yseiteraunavezmas.Cuandonohaymasvalores,elciclofortermina.

Ennuestroejemplo,0..10esunaexpresiónquetomaunaposicióndeinicioyunadefin,ydevuelveuniteradorporsobreesosvalores.Ellimitesuperioresexclusivo,entonces,nuestroloopimprimiráde0hasta9,no10.

RutnoposeeelcicloforalestilodeC,aproposito.Controlarmanualmentecadaelementodelcicloescomplicadoypropensoaerrores,inclusoparaprogramadoresCexperimentados.

ElLenguajedeProgramacionRust

163Ciclos

Enumerate

Cuandonecesitasllevarregistrodecuantasveceshasiterado,puedesusarlafunción.enumerate().

Enrangos:

for(i,j)in(5..10).enumerate(){

println!("i={}yj={}",i,j);

}

Salida:

i=0yj=5

i=1yj=6

i=2yj=7

i=3yj=8

i=4yj=9

Noolvidescolocarlosparéntesisalrededordelrango.

Eniteradores:

#letlineas="hola\nmundo".lines();

for(numero_linea,linea)inlineas.enumerate(){

println!("{}:{}",numero_linea,linea);

}

Outputs:

0:Contenidodelalineauno

1:Contenidodelalineados

2:Contenidodelalineatres

3:Contenidodelalineacuatro

FinalizandolaiteracióndemaneratempranaEchemosunvistazoaeseciclowhilequevimosconanterioridad:

ElLenguajedeProgramacionRust

164Ciclos

letmutx=5;

letmutcompletado=false;

while!completado{

x+=x-3;

println!("{}",x);

ifx%5==0{

completado=true;

}

}

Necesitamosmantenerunbindingavariablemut,completado,parasabercuandodebíamossalirdelciclo.Rustposeedospalabrasclaveparaayudarnosamodificarelprocesodeiteración:breakycontinue.

Enestecaso,podemosescribirelciclodeunamejorformaconbreak:

letmutx=5;

loop{

x+=x-3;

println!("{}",x);

ifx%5==0{break;}

}

Ahoraiteramosdemaneraindefinidaconloopyusamosbreakpararomperelciclodemaneratemprana.Usarunreturnexplícitotambiénsirveparalaterminacióntempranadelciclo.

continueessimilar,peroenlugardeterminarelciclo,noshaceiralasiguienteiteración.Losiguienteimprimirálosnumerosimpares:

forxin0..10{

ifx%2==0{continue;}

println!("{}",x);

}

Etiquetasloop

ElLenguajedeProgramacionRust

165Ciclos

Tambiénpodríasencontrarsituacionesenlascualestengasciclosanidadosynecesitesespecificaracualdelosciclospertenecentussentenciasbreakocontinue.Comoenlamayoríadeloslenguajes,pordefectounbreakocontinueaplicaaelciclomasinterno.Enelcasodequedesearasaplicarunbreakocontinueparaalgunodelosciclosexternos,puedesusaretiquetasparaespecificaracualcicloaplicalasentenciabreakocontinue.Losiguientesoloimprimirácuandoambosxyyseanimpares:

'exterior:forxin0..10{

'interior:foryin0..10{

ifx%2==0{continue'exterior;}//continuaelcicloporencimadex

ify%2==0{continue'interior;}//continuaelcicloporencimadey

println!("x:{},y:{}",x,y);

}

}

ElLenguajedeProgramacionRust

166Ciclos

%Pertenencia

EstaguíaesunadelastrespresentandoelsistemadepertenenciadeRust.EsteesunadelascaracterísticasmasúnicaseirresistiblesdeRust,conlaquedesarrolladoresRustdebenestarfamiliarizados.AtravésdelapertenenciaescomoRustlograsuobjetivomasimportante,laseguridad.Existenunospocosconceptosdistintos,cadaunoconsupropiocapitulo:

pertenencia,laqueleesactualmentepréstamo,ysucaracterísticaasociada‘referencias’tiempodevida,unconceptoavanzadodelpréstamo

Estostrescapítulosestánrelacionados,enorden.NecesitaraslostresparaentendercompletamenteelsistemadepertenenciadeRust.

MetaAntesdeentrarendetalle,dosnotasimportantesacercadelsistemadepertenencia.

Rusttienefocoenseguridadyvelocidad.Rustlograesosobjetivosatravezdemuchas‘abstraccionesdecerocosto’,loquesignificaqueenRust,lasabstraccionescuestantanpococomoseaposibleparahacerlasfuncionar.Elsistemadepertenenciaesunejemploprimordialdeunaabstraccióndecerocosto.Todoelanálisisdelqueestaremoshablandoenlapresenteguíaesllevadoacaboentiempodecompilación.Nopagasningúncostoentiempodeejecuciónporningunadeestasfacilidades.

Sinembargo,estesistematieneciertocosto:lacurvadeaprendizaje.MuchosusuariosnuevosRustexperimentanalgoquenosotrosdenominamos‘pelearconelcomprobadordepréstamo’(‘fightingwiththeborrowchecker’),situaciónenlacualelcompiladordeRustserehusaacompilarunprogramaelcualelautorpiensavalido.EstoocurreconfrecuenciadebidoaqueelmodelomentaldelprogramadoracercadecomofuncionalapertenencianoconcuerdaconlasreglasactualesimplementadasenRust.Probablementetuexperimentescosassimilaresalcomienzo.Sinembargo,haybuenasnoticias:otrosdesarrolladoresRustexperimentadosreportanqueunavezquetrabajanconlasreglasdelsistemadepertenenciaporunperiododetiempo,peleancadavezmenosconelcomprobadordepréstamo.

Conesoenmente,aprendamosacercadelapertenencia.

Pertenencia

ElLenguajedeProgramacionRust

167Pertenencia

LosBindingsavariableposeenunapropiedadenRust:Estos‘poseenpertenencia’sobreloqueestánasociados.Loquesetraduceaquecuandounbindingavariablesaledeámbito,Rustliberalosrecursosasociadosaeste.Porejemplo:

fnfoo(){

letv=vec![1,2,3];

}

Cuandoventraenámbito,unnuevoVec<T>escreado.Enestecasoelvectortambiénasignaalgodememoriadesdeelmontículo,paralostreselementos.Cuandovsaledeámbitoalfinaldefoo(),Rustlimpiaratodolorelacionadoalvector,incluyendolamemoriaasignadadesdeelmontículo.Estoocurredemaneradeterminista,alfinaldeelámbito.

SemanticademovimientoHayalgomassutilacá,Rustseaseguraquesoloexistaexactamenteunbindingacualquierrecursoenparticular.Porejemplo,sitenemosunvector,podemosasignarloaotrobindingavariable:

letv=vec![1,2,3];

letv2=v;

Pero,siintentamosusarvdespués,obtenemosunerror:

letv=vec![1,2,3];

letv2=v;

println!("v[0]es:{}",v[0]);

Elerrorlucecomoeste:

error:useofmovedvalue:`v`(usodevalormovido:`v`)

println!("v[0]es:{}",v[0]);

^

Algosimilarocurresidefinimosunafunciónquetomepertenencia,ytratamosdeusaralgodespuésdehabérselopasadocomoargumento:

ElLenguajedeProgramacionRust

168Pertenencia

fntomar(v:Vec){

//loquesucedeacánoesrelevante

}

letv=vec![1,2,3];

tomar(v);

println!("v[0]es:{}",v[0]);

Elmismoerror:‘useofmovedvalue’(usodevalormovido).Cuandotransferimoslapertenenciaaalgo,decimosquehemos‘movido’lacosaalacualnosestamosrefiriendo.Nonecesitasningúntipodeanotaciónespecialparaello,esloqueRusthacepordefecto.

LosdetallesLarazónporlacualnopodemosusarunbindingavariabledespuésdehaberlomovidoessutil,peroimportante.Cuandoescribimoscódigocomoeste:

letv=vec![1,2,3];

letv2=v;

Laprimeralineaasignamemoriaparaelobjetovector,v,yparaladataquecontiene.Elobjetovectoresentoncesalmacenadoenlapilapilaycontieneunapuntadoraelcontenido([1,2,3])almacenadoenelmonticulo.Cuandomovemosvav2,secreaunacopiadedichoapuntadorparav2.Todoestosignificaqueexistiríandosapuntadoresparaelcontenidodelvectorenelmontículo.LocualviolalasgarantíasdeseguridaddeRust,introduciendounacondicióndecarrera.EsporelloqueRustprohibeelusodevdespuésqueelmovimientohasidollevadoacabo.

Esimportantedestacarquealgunasoptimizacionespodríanremoverlacopiadelosbytesenlapila,dependiendodeciertascircunstancias.Asíquepuedenosertanineficienteacomoluceinicialmente.

TiposCopyHemosestablecidoquecuandotransferimoslapertenenciaaotrobindingavariable,nopodemosusarelbindingoriginal.Sinembargo,existeuntraittraitquecambiaestecomportamiento,sellamaCopy.Nohemosdiscutidolostraits(rasgos)todavía,peropor

ElLenguajedeProgramacionRust

169Pertenencia

ahorapuedesverloscomounaanotaciónhechaauntipoenparticularlacualagregacomportamientoextra.Porejemplo:

letv=1;

letv2=v;

println!("ves:{}",v);

Enestecaso,vesuni32,queimplementaeltraitCopy.Estosignificaque,justocomoenunmovimiento,cuandoasignamosvav2,unacopiadeladataeshecha.Pero,adiferenciadeloqueocurreenunmovimiento,podemoshacerusodevdespués.Estoesdebidoaqueuni32noposeeapuntadoresadataenningúnotrolugar,copiarlossignificacopiadocompleto.

TodoslostiposprimitivosimplementaneltraitCopyysupertenencianoesmovidacomounopodríaasumir,siguiendolasreglasdepertenencia.Comoejemplo,lossiguientesdospedazosdecódigosolocompilanporquelostiposi32yboolimplementaneltraitCopy.

fnmain(){

leta=5;

let_y=doblar(a);

println!("{}",a);

}

fndoblar(x:i32)->i32{

x*2

}

fnmain(){

leta=true;

let_y=cambiar_verdad(a);

println!("{}",a);

}

fncambiar_verdad(x:bool)->bool{

!x

}

DehabertenidotiposquenoimplementaseneltraitCopy,hubiésemosobtenidounerrordecompilaciónportratardeusarunvalormovido.

ElLenguajedeProgramacionRust

170Pertenencia

error:useofmovedvalue:`a`

println!("{}",a);

^

DiscutiremoscomohacerquetuspropiostiposseanCopyenlaseccióndetraits

MasquepertenenciaPorsupuesto,sinecesitaremosdevolverlapertenenciaconcadafunciónqueescribiésemos:

fnfoo(v:Vec<i32>)->Vec<i32>{

//haceralgoconv

//devolviendopertenencia

v

}

Lascosassevolveríanbastantetediosas.Seponeaunpeormientrastengamosmascosassobrelascualesqueramostenerpertenencia:

fnfoo(v1:Vec<i32>,v2:Vec<i32>)->(Vec<i32>,Vec<i32>,i32){

//haceralgoconv1yv2

//devolviendopertenencia,asícomoelresultadodenuestrafunción

(v1,v2,42)

}

letv1=vec![1,2,3];

letv2=vec![1,2,3];

let(v1,v2,respuesta)=foo(v1,v2);

Ugh!Eltipoderetorno,lalineaderetorno,yelllamadoalafunciónsevuelvenmuchomascomplicados.

Porsuerte,Rustofreceunafacilidad,elpréstamo,facilidadquenossirveparasolucionaresteproblema.

Eseltópicodelasiguientesección!

ElLenguajedeProgramacionRust

171Pertenencia

%ReferenciasyPréstamo

EstaguíaesunadelastrespresentandoelsistemadepertenenciadeRust.EstaesunadelascaracterísticasmasúnicasyatractivasdeRust,conlaquelosdesarrolladoresRustdebenestarbienfamiliarizados.LapertenenciaescomoRustlograsuobjetivomayor,seguridadenelmanejodememoria.Existenunospocosconceptosdistintos,cadaunoconsupropiocapitulo:

pertenencia,elconceptoprincipalprestamo,elqueleesahoratiemposdevida,unconceptoavanzadodelpréstamo

Estostrescapítulosestánrelacionados,yenorden.Necesitarasleerlostresparaentendercompletamenteelsistemadepertenencia.

MetaAntesdeentrarendetalle,dosnotasimportantesacercadelsistemadepertenencia.

Rusttienefocoenseguridadyvelocidad.Rustlograesosobjetivosatravezdemuchas‘abstraccionesdecerocosto’,loquesignificaqueenRust,lasabstraccionescuestantanpococomoseaposibleparahacerlasfuncionar.Elsistemadepertenenciaesunejemploprimordialdeunaabstraccióndecerocosto.Todoelanálisisdelqueestaremoshablandoenlapresenteguíaesllevadoacaboentiempodecompilación.Nopagasningúncostoentiempodeejecuciónporningunadeestasfacilidades.

Sinembargo,estesistematieneciertocosto:lacurvadeaprendizaje.MuchosusuariosnuevosRustexperimentanalgoquenosotrosdenominamos‘pelearconelcomprobadordepréstamo’(‘fightingwiththeborrowchecker’),situaciónenlacualelcompiladordeRustserehusaacompilarunprogramaelcualelautorpiensavalido.EstoocurreconfrecuenciadebidoaqueelmodelomentaldelprogramadoracercadecomofuncionalapertenencianoconcuerdaconlasreglasactualesimplementadasenRust.Probablementetuexperimentescosassimilaresalcomienzo.Sinembargo,haybuenasnoticias:otrosdesarrolladoresRustexperimentadosreportanqueunavezquetrabajanconlasreglasdelsistemadepertenenciaporunperiododetiempo,peleancadavezmenosconelcomprobadordepréstamo.

Conesoenmente,aprendamosacercadeelpréstamo.

Préstamo

ElLenguajedeProgramacionRust

172ReferenciasyPréstamo

Alfinaldelaseccióndepertenencia,teníamosunafunciónfeaqueluciaasí:

fnfoo(v1:Vec<i32>,v2:Vec<i32>)->(Vec<i32>,Vec<i32>,i32){

//haceralgoconv1yv2

//devolviendopertenencia,asícomoelresultadodenuestrafunción

(v1,v2,42)

}

letv1=vec![1,2,3];

letv2=vec![1,2,3];

let(v1,v2,respuesta)=foo(v1,v2);

Loanterior,sinembargo,noesRustidiomatico,debidoaquenosebeneficiadelasventajasdelpréstamo.Heaquielprimerpaso:

fnfoo(v1:&Vec<i32>,v2:&Vec<i32>)->i32{

//haceralgoconv1yv2

//retornandolarespuesta

42

}

letv1=vec![1,2,3];

letv2=vec![1,2,3];

letrespuesta=foo(&v1,&v2);

//podemosusarav1yv2aqui

EnlugardetomarVec<i32>scomoargumentos,tomamosunareferencia:&Vec<i32>.Yenlugardepasarv1yv2directamente,pasamos&v1y&v2.Llamamosaltipo&Tuna'referencia',yenvezdetomarpertenenciasobreelrecurso,estelatomaprestado.Unenlaceavariablequehaceunpréstamodealgonoliberaelrecursocuandosaledeámbito.Estosignificaquedespuésdelallamadaafoo(),podemosnuevamentehacerusodelosenlacesavariableoriginales.

Lasreferenciassoninmutables,justocomolosenlacesavariable.Estosetraduceaquedentrodefoo(),losvectoresnopuedensercambiados:

ElLenguajedeProgramacionRust

173ReferenciasyPréstamo

fnfoo(v:&Vec){

v.push(5);

}

letv=vec![];

foo(&v);

fallacon:

error:cannotborrowimmutableborrowedcontent`*v`asmutable

v.push(5);

^

Insertarunvalorcausaunamutaciónenelvector,ynotenemospermitidohacerlo.

referencias&mutExisteunsegundotipodereferencia:&mutT.Una‘referenciamutable’quepermitemutarelrecursoqueestastomandoprestado.Porejemplo:

letmutx=5;

{

lety=&mutx;

*y+=1;

}

println!("{}",x);

Loanteriorimprimirá6.Hacemosayunareferenciamutableax,entoncessumamosunoaloqueseaqueyapunta.Notarasquextambiéntuvoquehabersidomarcadocomomut,delocontrario,nohubiésemospodidotomarunpréstamomutableaunvalorinmutable.

Notarastambiénquehemosagregadounasterisco(*)alfrentedey,tornándoloen*y,estoesdebidoaqueyesunareferencia&mut.Tambiénnecesitarashacerusodeellosparaaccederaelcontenidodeunareferencia.

Deotromodo,lasreferencias&mutsoncomolasreferencias.Existeunagrandiferenciaentrelasdos,ycomoestasinteractuan.Habrásnotadoqueexistealgoquenohuelemuybienenelejemploanterior,puestoquenecesitamoseseámbitoextra,conlos{y}.Silosremovemos,obtenemosunerror:

ElLenguajedeProgramacionRust

174ReferenciasyPréstamo

error:cannotborrow`x`asimmutablebecauseitisalsoborrowedasmutable

println!("{}",x);

^

note:previousborrowof`x`occurshere;themutableborrowprevents

subsequentmoves,borrows,ormodificationof`x`untiltheborrowends

lety=&mutx;

^

note:previousborrowendshere

fnmain(){

}

^

Alparecer,hayreglas.

LasReglasHeaquilasreglasacercadelpréstamoenRust:

Primero,cualquierpréstamodebevivirenunámbitonomayoraldeeldueño.Segundo,puedestenerunouotrodeestostiposdepréstamo,peronolosdosalmismotiempo:

unaomasreferencias(&T)aunrecurso,exactamenteunareferenciamutable(&mutT).

Posiblementenotesqueestoesmuysimilar,peronoexactamenteigual,aladefinicióndeunacondicióndecarrera:

Hayuna‘condicióndecarrera’cuandodosomasapuntadoresaccedenalamismalocaciónenmemoriaalmismotiempo,endondealmenosunoestaescribiendo,ylasoperacionesnoestánsincronizadas.

Conlasreferencias,puedestenercuantasdesees,debidoaqueningunadeellasestaescribiendo.Siestasescribiendo,ynecesitasdosomasapuntadoresalamismamemoria,puedestenersoloun&mutalavez.EsasícomoRustprevienelascondicionesdecarreraentiempodecompilación:obtendremoserroressirompemoslasreglas.

Conestoenmente,consideremosnuestroejemplootravez.

PensandoenámbitosHeaquielcódigo:

ElLenguajedeProgramacionRust

175ReferenciasyPréstamo

letmutx=5;

lety=&mutx;

*y+=1;

println!("{}",x);

Elcódigoanteriorgeneraelsiguienteerror:

error:cannotborrow`x`asimmutablebecauseitisalsoborrowedasmutable

println!("{}",x);

^

Estoesdebidoaquehemosvioladolasreglas:tenemosun&mutTapuntandoax,yenconsecuencianotenemospermitidocrearningún&Ts.Unacosauotra.Lanotaapuntaacomopensaracercadeesteproblema:

note:previousborrowendshere

fnmain(){

}

^

Enotraspalabras,elpréstamomutableesmantenidoalolargodeelrestodenuestroejemplo.Loquequeremosesquenuestropréstamomutabletermineantesqueintentemosllamaraprintln!yhagamosunpréstamoinmutable.EnRust,elpréstamoestaasociadoalámbitoenelcualelpréstamoesvalido.Nuestrosámbitoslucenasí:

letmutx=5;

lety=&mutx;//-+préstamo&mutdexcomienzaaqui

//|

*y+=1;//|

//|

println!("{}",x);//-+-intentodetomarprestadoxaqui

//-+préstamo&mutdexterminaaqui

Losámbitosentranenconflicto:nopodemoscrearun&xmientrasyestaenámbito.

Entoncescuandoagregamosllaves:

ElLenguajedeProgramacionRust

176ReferenciasyPréstamo

letmutx=5;

{

lety=&mutx;//-+préstamo&mutdexcomienzaaqui

*y+=1;//|

}//-+...yterminaaqui

println!("{}",x);//<-intentodetomarprestadoxaqui

Nohayproblema.Nuestropréstamomutablesaledeámbitoantesdequecreemosunpréstamoinmutable.Elámbitoesclaveparavercuantoduraelpréstamo.

ProblemasqueelpréstamoprevienePorquetenemosestasreglasrestrictivas?Bueno,comolonotamos,estasreglasprevienencondicionesdecarrera.Quetiposdeproblemascausanlascondicionesdecarrera?Acáunospocos.

InvalidacióndeIteradores

Unejemploesla‘invalidacióndeiteradores’,queocurrecuandotratasdemutarunacolecciónmientrasestasiterandosobreella.ElcomprobadordeprestamosdeRustevitaqueestoocurra:

letmutv=vec![1,2,3];

foriin&v{

println!("{}",i);

}

Loanteriorimprimedesdeunohastatres.Amedidaqueiteramoslosvectores,solosenosproporcionanreferenciasasuselementos.vensimismoestomadoprestadodemanerainmutable,loquesetraduceenquenopodamoscambiarlomientrasloiteramos:

letmutv=vec![1,2,3];

foriin&v{

println!("{}",i);

v.push(34);

}

Heaquielerror:

ElLenguajedeProgramacionRust

177ReferenciasyPréstamo

error:cannotborrow`v`asmutablebecauseitisalsoborrowedasimmutable

v.push(34);

^

note:previousborrowof`v`occurshere;theimmutableborrowprevents

subsequentmovesormutableborrowsof`v`untiltheborrowends

foriin&v{

^

note:previousborrowendshere

foriin&v{

println!(“{}”,i);

v.push(34);

}

^

Nopodemosmodificarvdebidoaqueestatomadoprestadoporelciclo.

usodespuesdeliberacion(useafterfree)

Lasreferenciasnodebenvivirpormastiempoqueelrecursoalcualestasapuntan.Rustchequearalosámbitosdetusreferenciasparaasegurarsedequeestoseacierto.

SiRustnoverificaraestapropiedad,podriamosaccidentalmenteusarunareferenciainvalida.Porejemplo:

lety:&i32;

{

letx=5;

y=&x;

}

println!("{}",y);

Obtenemoselsiguienteerror:

ElLenguajedeProgramacionRust

178ReferenciasyPréstamo

error:`x`doesnotlivelongenough

y=&x;

^

note:referencemustbevalidfortheblocksuffixfollowingstatement0at

2:16...

lety:&i32;

{

letx=5;

y=&x;

}

note:...butborrowedvalueisonlyvalidfortheblocksuffixfollowing

statement0at4:18

letx=5;

y=&x;

}

Enotraspalabras,yesvalidosoloparaelámbitoendondexexiste.Tanprontocomoxseva,sehaceinvalidohacerlereferencia.Esporelloqueelerrordicequeelpréstamo,‘novivelosuficiente’(‘doesn’tlivelongenough’)yaquenoesvalidoporlacantidaddetiempocorrecta.

Elmismoproblemaocurrecuandolareferenciaesdeclaradaantesdelavariablealacualhacereferencia.Estoesdebidoaquelosrecursosdentrodelmismoámbitosonliberadosenordenopuestoalordenenelquefuerondeclarados:

lety:&i32;

letx=5;

y=&x;

println!("{}",y);

Obtenemosesteerror:

ElLenguajedeProgramacionRust

179ReferenciasyPréstamo

error:`x`doesnotlivelongenough

y=&x;

^

note:referencemustbevalidfortheblocksuffixfollowingstatement0at

2:16...

lety:&i32;

letx=5;

y=&x;

println!("{}",y);

}

note:...butborrowedvalueisonlyvalidfortheblocksuffixfollowing

statement1at3:14

letx=5;

y=&x;

println!("{}",y);

}

Enelejemploanterior,yesdeclaradaantesquex,significandoqueyvivemasquex,locualnoestapermitido.

ElLenguajedeProgramacionRust

180ReferenciasyPréstamo

%TiemposdeVida

EstaguíaesunadelastrespresentandoelsistemadepertenenciadeRust.EstaesunadelascaracterísticasmasúnicasyatractivasdeRust,conlaquelosdesarrolladoresRustdebenestarbienfamiliarizados.LapertenenciaescomoRustlograsuobjetivomayor,seguridadenelmanejodememoria.Existenunospocosconceptosdistintos,cadaunoconsupropiocapitulo:

pertenencia,elconceptoprincipal[préstamo][borrowing],ysuscaracterísticaasociada‘referencias’tiemposdevida,laqueleesahora

Estostrescapítulosestánrelacionados,yenorden.Necesitarasleerlostresparaentendercompletamenteelsistemadepertenencia.

MetaAntesdeentrarendetalle,dosnotasimportantesacercadelsistemadepertenencia.

Rusttienefocoenseguridadyvelocidad.Rustlograesosobjetivosatravezdemuchas‘abstraccionesdecerocosto’,loquesignificaqueenRust,lasabstraccionescuestantanpococomoseaposibleparahacerlasfuncionar.Elsistemadepertenenciaesunejemploprimordialdeunaabstraccióndecerocosto.Todoelanálisisdelqueestaremoshablandoenlapresenteguíaesllevadoacaboentiempodecompilación.Nopagasningúncostoentiempodeejecuciónporningunadeestasfacilidades.

Sinembargo,estesistematieneciertocosto:lacurvadeaprendizaje.MuchosusuariosnuevosRustexperimentanalgoquenosotrosdenominamos‘pelearconelcomprobadordepréstamo’(‘fightingwiththeborrowchecker’),situaciónenlacualelcompiladordeRustserehusaacompilarunprogramaelcualelautorpiensavalido.EstoocurreconfrecuenciadebidoaqueelmodelomentaldelprogramadoracercadecomofuncionalapertenencianoconcuerdaconlasreglasactualesimplementadasenRust.Probablementetuexperimentescosassimilaresalcomienzo.Sinembargo,haybuenasnoticias:otrosdesarrolladoresRustexperimentadosreportanqueunavezquetrabajanconlasreglasdelsistemadepertenenciaporunperiododetiempo,peleancadavezmenosconelcomprobadordepréstamo.

Conesoenmente,aprendamosacercadelostiemposdevida.

Tiemposdevida

ElLenguajedeProgramacionRust

181TiemposdeVida

Prestarunareferenciaaotrorecursodelquealguienmasesdueñopuedesercomplicado.Porejemplo,imaginaesteconjuntodeoperaciones:

Obtengounhandleaalgúntipoderecurso.Teprestounareferenciaaelrecurso.Decidoqueheterminadoconelrecurso,ylolibero,mientrastodavíatieneslareferenciaael.Tudecidesusarelrecurso.

Ohno!Tureferenciaestaapuntandoaunrecursoinvalido.Estoesllamadounpunterocolganteousodespuésdeliberación,cuandoelrecursoesmemoria.

Paraarreglaresto,tenemosqueasegurarnosqueelpasocuatronuncaocurradespuésdelpasotres.ElsistemadepertenenciadeRustllevaestoacaboatravésdeunconceptodenominadotiemposdevida,loscualesdescribenelámbitoenelcualunareferenciaesvalida.

Cuandotenemosunafunciónquetomaunareferenciacomoargumento,podemosserimplícitosoexplícitosacercadeltiempodevidadelareferencia:

//implicito

fnfoo(x:&i32){

}

//explicito

fnbar<'a>(x:&'ai32){

}

El'aselee‘eltiempodevidaa’.Técnicamente,todareferenciaposeeuntiempodevidaasociadoaella,peroelcompiladortepermiteomitirlasencasoscomunes.Antesquelleguemosaeso,analicemoselpedazodecódigoexplícito:

fnbar<'a>(...)

Anteriormentehablamosunpocoacercadelasintaxisdefunciones,peronodiscutimoslos<>sdespuésdeunnombredefunción.Unafunciónpuedetener‘parámetrosgenéricos’entrelos<>s,ylostiemposdevidasonuntipodeparámetrogenérico.Discutiremosotrostiposdegenericosmastardeenellibro,peroporahora,enfoquémonossoloenelaspectodelostiemposdevida.

Usamos<>paradeclararnuestrostiemposdevida.Estodicequebarposeeuntiempodevida,'a.Dehabertenidoreferenciascomoparámetros,hubieselucidodeestamanera:

ElLenguajedeProgramacionRust

182TiemposdeVida

fnbar<'a,'b="">(...)

Entoncesennuestralistadeparámetros,usamoslostiemposdevidaquehemosnombrado:

...(x:&'ai32)

Dehaberqueridounareferencia&mut,pudimoshaberhecholosiguiente:

...(x:&'amuti32)

Sicomparas&muti32con&'amuti32,sonlomismo,essoloqueeltiempodevida'asehametidoentreel&yelmuti32.Leemos&muti32como‘unareferenciamutableauni32’y&'amuti32como‘unareferenciamutableauni32coneltiempodevida'a’.

En structsTambiénnecesitarastiemposdevidaexplícitoscuandotrabajesconstructs:

structFoo<'a>{

x:&'ai32,

}

fnmain(){

lety=&5;//estoeslomismoque`let_y=5;lety=&_y;`

letf=Foo{x:y};

println!("{}",f.x);

}

Comopuedesver,losstructspuedentambiéntenertiemposdevida.Enunaformasimilaralasfunciones,

structFoo<'a>{

#x:&'ai32,

#}

declarauntiempodevida,y

ElLenguajedeProgramacionRust

183TiemposdeVida

#structFoo<'a>{

x:&'ai32,

#}

haceusodeel.Entonces,porquenecesitamosuntiempodevidaaquí?NecesitamosasegurarnosquecualquierreferenciaaunFoonopuedavivirmasquelareferenciaauni32queestecontiene.

bloquesimplImplementemosunmetodoenFoo:

structFoo<'a>{

x:&'ai32,

}

impl<'a>Foo<'a>{

fnx(&self)->&'ai32{self.x}

}

fnmain(){

lety=&5;//estoeslomismoque`let_y=5;lety=&_y;`

letf=Foo{x:y};

println!("xes:{}",f.x());

}

Comopuedesver,necesitamosdeclararuntiempodevidaparaFooenlalineaimpl.Repetimos'adosveces,justocomoenfunciones:impl<'a>defineuntiempodevida'a,yFoo<'a>haceusodeel.

MultiplestiempodevidaSiposeesmultiplesreferencias,puedeshacerusodeelmismotiempodevidamultiplesveces:

fnx_o_y<'a>(x:&'astr,y:&'astr)->&'astr{

#x

#}

ElLenguajedeProgramacionRust

184TiemposdeVida

Loanteriordicequeambosxyyvivenporelmismoámbito,yqueelvalorderetornotambiénestavivoparadichoámbito.Sihubiesesqueridoquexyytuviesendiferentestiemposdevida,pudistehaberhechousodemultiplesparámetrosdetiemposdevida:

fnx_o_y<'a,'b>(x:&'astr,y:&'bstr)->&'astr{

#x

#}

Enesteejemplo,xyytienendiferentesámbitosvalidos,peroelvalorderetornotieneelmismotiempodevidaquex.

PensandoenámbitosUnaformadepensaracercadelostiemposdevidaesvisualizarelámbitoenelcualesvalidaunareferencia.Porejemplo:

fnmain(){

lety=&5;//-+yentraenambito

//|

//stuff//|

//|

}//-+ysaledeambito

AgregandonuestroFoo:

structFoo<'a>{

x:&'ai32,

}

fnmain(){

lety=&5;//-+yentraenambito

letf=Foo{x:y};//-+fentraenambito

//stuff//|

//|

}//-+fyysalendeambito

Nuestrofvivedentrodeelámbitodey,esporelloquetodofunciona.Quepasaríadelocontrario?Elsiguientecódigonofuncionaria:

ElLenguajedeProgramacionRust

185TiemposdeVida

structFoo<'a>{

x:&'ai32,

}

fnmain(){

letx;//-+xentraenambito

//|

{//|

lety=&5;//---+yentraenambito

letf=Foo{x:y};//---+fentraenambito

x=&f.x;//||erroraqui

}//---+fyysalendeambito

//|

println!("{}",x);//|

}//-+xsaledeambito

Uff!Comopuedesveraqui,losámbitosdefyysonmenoresqueelámbitodex.Perocuandohacemosx=&f.x,hacemosaxunareferenciaaalgoqueestarporsalirdeámbito.

Lostiemposdevidaconnombresonunaformadedarlesadichosámbitosunnombre.Darleunnombreaalgoeselprimerpasohaciapoderhablaracercadeel.

'staticEltiempodevidadenominado‘static’esuntiempodevidaespecial.Esteseñalaquealgoposeeeltiempodevidadeelprogramacompleto.LamayoríadelosdesarrolladoresRustconocena'staticcuandolidianconcadenasdecaracteres:

letx:&'staticstr="Hola,mundo.";

Losliteralesdecadenasdecaracteresposeeneltipo&'staticstrpuestoquelareferenciaestasiempreviva:estossoncolocadosenelsegmentodedatosdelbinariofinal.Otroejemplosonlasglobales:

staticFOO:i32=5;

letx:&'statici32=&FOO;

Loanterioragregauni32aelsegmentodedatosdeelbinario,yxesunareferenciaael.

Elisiondetiemposdevida

ElLenguajedeProgramacionRust

186TiemposdeVida

Rustsoportaunainferenciadetipospoderosaenloscuerposdefunción,peroestaprohibidoenlasfirmasdeelementospermitirrazonamientobasadoúnicamenteenlafirma.Sinembargo,porrazonesergonomicas,unainferenciasecundariamuyrestrictallamada“elisiondetiemposdevida”seaplicaenlasfirmasdefunción.La“elisiondetiemposdevida”infierebasandosesoloenloscomponentesdelafirmasinbasarseenelcuerpodelafunción,unicamneteinfiereparámetrosdetiemposdevida,yhaceestoconsolotresreglasfácilmentememorizableseinambiguas.Todoestohacealaelisiondetiemposdevidaunatajoparaescribirunafirma,sinnecesidaddeocultarlostiposinvolucradospuestoaqueinferencialocalcompletaseraaplicadaaellos.

Cuandosehabladeelisiondetiemposdevida,usamoselterminotiempodevidadeentradaytiempodevidadesalida.Untiempodevidadeentradaesuntiempodevidaasociadoconunparámetrodeunafunción,yuntiempodevidadesalidaesuntiempodevidaasociadoconelvalorderetornodeunafunción.Porejemplo,lasiguientefuncióntieneuntiempodevidadeentrada:

fnfoo<'a>(bar:&'astr)

Estaposeeuntiempodevidadesalida:

fnfoo<'a>()->&'astr

Lasiguientetieneuntiempodevidaenambasposiciones:

fnfoo<'a>(bar:&'astr)->&'astr

Heaquilastresreglas:

Cadatiempodevidaelididoenlosargumentosdeunafunciónseconvierteenunparámetrodetiempodevidadistinto.

Siexisteexactamenteunsolotiempodevidadeentrada,elididoono,esetiempodevidaesasignadoatodoslostiemposdevidaelididosenlosvaloresderetornodeesafunción.

Siexistenmultiplestiemposdevidadeentrada,perounadeelloses&selfo&mutself,eltiempodevidadeselfesasignadoatodoslostiemposdevidadesalidaelididos.

Delocontrario,esunerrorelidiruntiempodevidadesalida.

Ejemplos

ElLenguajedeProgramacionRust

187TiemposdeVida

Heaquialgunosejemplosdefuncionescontiemposdevidaelididos.Hemospareadocadaejemplodeuntiempodevidaelididoconsuformaexpandida.

fnprint(s:&str);//elidido

fnprint<'a>(s:&'astr);//expandido

fndebug(lvl:u32,s:&str);//elidido

fndebug<'a>(lvl:u32,s:&'astr);//expandido

//Enelejemploanterior,`lvl`nonecesitauntiempodevidadebidoaquenoesunareferencia(`&`).Sololascosasrelacionadasconreferencias(comoun`struct`quecontieneunareferencia)necesitantiemposdevida.

fnsubstr(s:&str,until:u32)->&str;//elidido

fnsubstr<'a>(s:&'astr,until:u32)->&'astr;//expandido

fnget_str()->&str;//ILLEGAL,noinputs

fnfrob(s:&str,t:&str)->&str;//ILEGAL,dosentradas

fnfrob<'a,'b="">(s:&'astr,t:&'bstr)->&str;//Expandido:Tiempodevidadesalidaesambiguo

fnget_mut(&mutself)->&mutT;//elidido

fnget_mut<'a>(&'amutself)->&'amutT;//expanded

fnargs(&mutself,args:&[T])->&mutCommand//elidido

fnargs<'a,'b,=""T:ToCStr="">(&'amutself,args:&'b[T])->&'amutCommand//expanded

fnnew(buf:&mut[u8])->BufWriter;//elidido

fnnew<'a>(buf:&'amut[u8])->BufWriter<'a>//expanded

ElLenguajedeProgramacionRust

188TiemposdeVida

%Mutabilidad

Mutabilidad,eslahabilidadqueunacosaposeeparasercambiada,funcionaunpocodiferenteenRustqueenotroslenguajes.Elprimeraspectodelamutabilidadesquenoestahabilitadapordefecto:

letx=5;

x=6;//error!

Podemosintroducirmutabilidadconlapalabrareservadamut:

letmutx=5;

x=6;//nohayproblema!

Estoesunenlaceavariablemutable.Cuandounenlaceavariableesmutable,significaquetienespermitidocambiaraloqueelenlaceapunta.Entonces,enelejemploanterior,noestacambiandoelvalorenx,encambio,elenlacecambiodeuni32aotro.

Sideseascambiaraqueapuntaelenlaceavariable,necesitarasunareferenciamutable:

letmutx=5;

lety=&mutx;

yesunenlaceavariableinmutableaunareferenciamutable,loquesignificaquenopuedesasociaryaotracosa(y=&mutz),peropuedesmutarloqueseaaloqueyestaasociado(*y=5).Unadiferenciamuysutil.

Porsupuesto,sinecesitasambascosas:

letmutx=5;

letmuty=&mutx;

Ahoraypuedeserasociadoaotrovalor,yelvalorqueestareferenciandopuedesercambiado.

Esimportantenotarquemutespartedeunpatron,demaneratalquepuedashacercosascomo:

let(mutx,y)=(5,6);

fnfoo(mutx:i32){

#}

ElLenguajedeProgramacionRust

189Mutabilidad

MutabilidadInteriorvs.MutabilidadExteriorSinembargo,cuandodecimosquealgoes‘immutable’enRust,estonosignificaquenopuedasercambiado:loquedecimosesquealgotiene‘mutabilidadexterior’.Considera,porejemplo,Arc<T>:

usestd::sync::Arc;

letx=Arc::new(5);

lety=x.clone();

Cuandollamamosaclone(),elArc<T>necesitaactualizarelcontadordereferencias.Apesardequenohemosusadoningúnmutaquí,xesunenlaceinmutable,tampocotomamos&mut5oalgunamas.Entonces,queestapasando?

Paraentenderesto,debemosvolveralnúcleodelafilosofíaqueguíaaRust,seguridadenelmanejodememoria,yelmecanismoatravésdelcualRustlagarantiza,elsistemadepertenencia,ymasespecíficamente,elpréstamo:

Puedestenerunouotrodeestosdostiposdeprestamo,peronolosdosalmismotiempo:

unaomasreferencias(&T)aunrecurso,exactamenteunareferenciamutable(&mutT).

Entonces,esaesladefiniciónrealde‘inmutabilidad’:essegurotenerdosapuntadores?EnelcasodeArc<T>’s,si:lamutaciónestacompletamentecontenidadentrodelaestructuraensimisma.Noestadisponiblealusuario.Porestarazón,retornaclone()&Ts.Siproporcionase&mutTs,seriaunproblema.

Otrostiposcomolosdelmodulostd::cell,poseenloopuesto:mutabilidadinterior.Porejemplo:

usestd::cell::RefCell;

letx=RefCell::new(42);

lety=x.borrow_mut();

RefCelproporcionareferencias&mutaloquecontienenatravesdelmetodoborrow_mut().Noesestopeligroso?Quetalsihacemos:

ElLenguajedeProgramacionRust

190Mutabilidad

usestd::cell::RefCell;

letx=RefCell::new(42);

lety=x.borrow_mut();

letz=x.borrow_mut();

#(y,z);

Esto,enefecto,harápánicoentiempodeejecución.EstoesloqueRefCellhace:aplicalasreglasdepréstamodeRustentiempodeejecución,yhacepanic!ossidichasreglassonvioladas.LoanteriornospermiteacercarnosaotroaspectodelasreglasdemutabilidaddeRust.Hablemosacercadeelloprimero.

MutabilidadaniveldecamposLamutabilidadesunapropiedaddeunpréstamo(&mut)ounenlaceavariable(letmut).Estosetraduceenque,porejemplo,nopuedestenerunstructconalgunoscamposmutablesyotrosinmutables:

structPunto{

x:i32,

muty:i32,//nope

}

Lamutabilidaddeunstructestaensuenlaceavariable:

structPunto{

x:i32,

y:i32,

}

letmuta=Punto{x:5,y:6};

a.x=10;

letb=Punto{x:5,y:6};

b.x=10;//error:cannotassigntoimmutablefield`b.x`

Sinembargo,usandoCell<T>,puedesemularmutabilidadaniveldecampos:

ElLenguajedeProgramacionRust

191Mutabilidad

usestd::cell::Cell;

structPunto{

x:i32,

y:Cell<i32>,

}

letpunto=Punto{x:5,y:Cell::new(6)};

punto.y.set(7);

println!("y:{:?}",punto.y);

Estoimprimiráy:Cell{value:7}.Hemosactualizadoydemanerasatisfactoria.

ElLenguajedeProgramacionRust

192Mutabilidad

%Estructuras(Structs)

Lasestructuras(structs)sonunaformadecreartiposdedatosmascomplejos.Porejemplo,siestuviéramoshaciendocálculosqueinvolucrarancoordenadasenunespacio2D,necesitaríamosambos,unvalorxyunvalory:

letorigen_x=0;

letorigen_y=0;

unastructnospermitecombinarambosenunúnicotipodedatosunificado:

structPunto{

x:i32,

y:i32,

}

fnmain(){

letorigen=Punto{x:0,y:0};//origin:Punto

println!("Elorigenestaen({},{})",origen.x,origen.y);

}

Haymuchascosaspasandoacá,asíqueanalicémosloporpartes.Declaramosunaestructuraconlapalabrareservadastruct,seguidadeunnombre.Porconvención,losstructscomienzanconunaletramayúsculaysoncamelcased:PuntoEnElEspacio,noPunto_En_El_Espacio.

Podemoscrearunainstanciadenuestrastructvialet,comoesusual,perousamosunasintaxisestiloclave:valorparaasignarcadacampo.Elordernonecesitaserelmismoqueenladeclaraciónoriginal.

Finalmente,debidoaquetenemosnombresdecampos,podemosaccederaellosatravésdelanotacióndepuntos:origen.x.

Losvaloresenstructssoninmutablespordefecto,justocomolosdemásenlacesavariablesenRust.Usamutparahacerlosmutables:

ElLenguajedeProgramacionRust

193Estructuras

structPunto{

x:i32,

y:i32,

}

fnmain(){

letmutpunto=Punto{x:0,y:0};

punto.x=5;

println!("Elorigenestaen({},{})",punto.x,punto.y);

}

EstoimprimiráElorigenestaen(5,0).

Rustnosoportamutabilidaddecamposaniveldelenguaje,esporelloquenopuedesescribiralgocomoesto:

structPunto{

mutx:i32,

y:i32,

}

Lamutabilidadesunapropiedaddelenlaceavariable,nodelaestructuraensimisma.Siestasacostumbradoalamutabilidadaniveldecampos,estopuedeparecerunpocoextrañoalprincipio,perosimplificalascosasdemanerasignificativa.Inclusotepermitehaceralascosasmutablessoloporunperiodocortodetiempo:

structPunto{

x:i32,

y:i32,

}

fnmain(){

letmutpunto=Punto{x:0,y:0};

punto.x=5;

letpunto=punto;//ahora,estenuevoenlaceavariablenopuedesercambiado

punto.y=6;//estocausaunerror

}

Sintaxisdeactualización

ElLenguajedeProgramacionRust

194Estructuras

Unastructpuedeincluir..paraindicarquedeseasusarunacopiadealgúnotrostructparaalgunosdelosvalores.Porejemplo:

structPunto3d{

x:i32,

y:i32,

z:i32,

}

letmutpunto=Punto3d{x:0,y:0,z:0};

punto=Punto3d{y:1,..punto};

Loanterior,asignaapuntounnuevoy,peromantienelosantiguosvaloresdexyz.Tampocotienequeserlamismaestructura,puedeshacerusodeestasintaxiscuandocreasnuevas,yestacopiaralosvaloresquenoespecifiques:

#structPunto3d{

#x:i32,

#y:i32,

#z:i32,

#}

letorigen=Punto3d{x:0,y:0,z:0};

letpunto=Punto3d{z:1,x:2,..origen};

Tuplaestructuras(Tuplestructs)Rustposeeotrotipodedatosqueescomounhíbridoentreunatuplayunastruct,llamadotullaestructura(tuplestruct).Lastuplaestructurasposeenunnombre,perosuscamposno:

structColor(i32,i32,i32);

structPunto(i32,i32,i32);

Lasdosanterioresnoserániguales,inclusosiposeenlosmismosvalores:

#structColor(i32,i32,i32);

#structPunto(i32,i32,i32);

letnegro=Color(0,0,0);

letorigen=Punto(0,0,0);

Casisiempreesmejorusarunastructqueunatuplestruct.Ensulugar,podríamosescribirColorandPuntoasí:

ElLenguajedeProgramacionRust

195Estructuras

structColor{

rojo:i32,

azul:i32,

verde:i32,

}

structPunto{

x:i32,

y:i32,

z:i32,

}

Ahora,tenemosnombres,enlugardeposiciones.Losbuenosnombressonimportantes,yconunastruct,tenemosnombres.

Hayuncasoenelcualunatuplaestructuraesmuyútil,yesunatuplaestructuraconunsoloelemento.Sedenominapatrónnuevotipo(newtype),puestoaquecrearunnuevotipo,distintoaelvalorquecontiene,expresandosupropiasemántica:

structPulgadas(i32);

letlongitud=Pulgadas(10);

letPulgadas(longitud_enteros)=longitud;

println!("lalongitudes{}pulgadas",longitud_enteros);

Comonotaras,puedesextraerelenterocontenidoconunletdedestructuración,justocomoenlastuplasregulares.Enestecaso,elletPulgadas(longitud_enteros)asigna10alongitud_enteros.

Estructurastipo-unitario(unit-likestructs)Puedesdefinirunastructsinningúnmiembro:

structElectron;

Dichastructesdenominadatipo-unitario(unit-like)porsusemejanzaalatuplavacía,(),algunasvecesllamadaunidad(unit).Comounatuplaestructura,defineunnuevotipo.

Loanterioresraramenteutilensimismo(aunquealgunasvecespuedeservircomountipomarcador),peroencombinaciónconotrascaracterísticas,puedetornarseútil.Porejemplo,unalibreríapuederequerircrearunaestructuraqueimplementaciertotraitparamanejar

ElLenguajedeProgramacionRust

196Estructuras

eventos.Denoposeerningunadataparaguardarenlaestructura,simplementepuedescrearunastructtipo-unitario.

ElLenguajedeProgramacionRust

197Estructuras

%Enumeraciones

Unaenumeración(enum)enRustesuntipoquerepresentadataquepuedeserunadeunconjuntodevariantesposible:

enumMensaje{

Salir,

CambiarColor(i32,i32,i32),

Mover{x:i32,y:i32},

Escribir(String),

}

Cadavariantepuedeopcionalmentetenerdataasociada.Lasintaxisparaladefinicióndevariantesessemejantealasintaxisusadaparadefinirestructuras(structs):puedestenervariantessindatos(comolasestructurastipo-unitario),variantescondatanombrada,yvariantescondatasinnombres(comotuplaestructuras).Sinembargo,adiferenciadelasdefinicionesdeestructuras,unenumesunúnicotipo.Unvalordeunenumpuedecoincidirconcualquieradelasvariantes.Porestarazón,unenumesdenominadoalgunasvecesun‘tiposuma’(‘sumtype’):elconjuntodevaloresposiblesdelenumeslasumadelosconjuntosdevaloresposiblesparacadavariante.

Utilizamoslasintaxis::parahacerusodecadavariante:lasvariantesestándentrodelámbitodelenum.Loquehacequelosiguienteseavalido:

#enumMensaje{

#Mover{x:i32,y:i32},

#}

letx:Mensaje=Mensaje::Mover{x:3,y:4};

enumTurnoJuegoMesa{

Mover{celdas:i32},

Pasar,

}

lety:TurnoJuegoMesa=TurnoJuegoMesa::Mover{celdas:1};

AmbasvariantesposeenelnombreMover,perodebidoaquesuámbitoesdentrodelnombredelenum,ambaspuedenserusadassinconflicto.

Unvalordeuntipoenumcontieneinformaciónacercadecualvariantees,enadiciónacualquierdataasociadacondichavariante.Estoesalgunasvecesdenominado‘unionetiquetada’(‘taggedunion’),puestoaqueladataincluyeuna‘etiqueta’(‘tag’)queindicaquetipoes.Elcompiladorusaestainformaciónparaasegurarsequeestemosaccediendoaladataenelenumdemanerasegura.Porejemplo,nopuedessimplementetratardedestructurarunvalorcomosiestefuereunadelasposiblesvariantes:

ElLenguajedeProgramacionRust

198Enumeraciones

fnprocesar_cambio_color(msj:Mensaje){

letMensaje::CambiarColor(r,v,a)=msj;//compile-timeerror

}

Nosoportarestetipodeoperacionespuedelucirunpocolimitante,peroesunalimitaciónquepodemossuperar.Existendosmaneras,implementandolaigualdadpornuestracuenta,oatravésdeelpatternmatchingconexpresionesmatch,queaprenderásenlasiguientesección.TodavíanosabemoslosuficientedeRustparaimplementarlaigualdadpornuestracuenta,peroloveremosenlaseccióntraits.

ConstructorescomofuncionesUnconstructordeunenumpuedesertambiénusadocomounafunción.Porejemplo:

#enumMensaje{

#Escribir(String),

#}

letm=Mensaje::Escribir("Hola,mundo".to_string());

Eslomismoque

#enumMensaje{

#Escribir(String),

#}

fnfoo(x:String)->Mensaje{

Mensaje::Escribir(x)

}

letx=foo("Hola,mundo".to_string());

Estonoesinmediatamenteutilparanosotros,perocuandolleguemosalosclosures,hablaremosacercadepasarfuncionescomoargumentosaotrasfunciones.Porejemplo,conlositeradores,podemosconvertirunvectordeStringsenunvectordeMessage::Escribirs:

#enumMensaje{

#Escribir(String),

#}

letv=vec!["Hola".to_string(),"Mundo".to_string()];

letv1:Vec<Mensaje>=v.into_iter().map(Mensaje::Escribir).collect();

ElLenguajedeProgramacionRust

199Enumeraciones

ElLenguajedeProgramacionRust

200Enumeraciones

%Match

Amenudo,unsimpleif/elsenoessuficiente,debidoaquetienesmasdedosopcionesposibles.También,lascondicionespuedensercomplejas.Rustposeeunapalabrareservada,match,quetepermitereemplazarconstruccionesif/elsecomplicadasconalgomaspoderoso.Echaunvistazo:

letx=5;

matchx{

1=>println!("uno"),

2=>println!("dos"),

3=>println!("trees"),

4=>println!("cuatro"),

5=>println!("cinco"),

_=>println!("otracosa"),

}

matchtomaunaexpresiónyluegobifurcabasadoensuvalor.Cadabrazodelaramatienelaformavalor=>expresión.Cuandoelvalorcoincide,laexpresióndelbrazoesevaluada.Esllamadomatchporeltermino‘patternmatching’,delcualmatchesunaimplementación.Hayunasecciónenteraacercadelospatronesquecubretodoslospatronesposibles.

Entonces,cualeslagranventaja?Bueno,hayunaspocas.Primeroquetodo,matchimponechequeodeagotamiento(‘exhaustivenesschecking’).Veselultimobrazo,elqueposeeelguiónbajo(_)?Siremovemosesebrazo,Rustnosproporcionaraunerror:

error:non-exhaustivepatterns:`_`notcovered

Enotraspalabras,Rustestatratandodedecirnosqueolvidamosunvalor.Debidoaquexesunentero,Rustsabequexpuedetenerunnumerodevaloresdiferentes-porejemplo,6.Sinel_,sinembargo,nohaybrazoquepuedacoincidir,yenconsecuenciaRustserehusaacompilarelcódigo._actúacomoun‘brazoqueatrapatodo’.Siningunodelosotrosbrazoscoincide,elbrazoconel_lohará,ypuestoquetenemosdicho‘brazoqueatrapatodo’,tenemosahoraunbrazoparacadavalorposibledex,ycomoresultado,nuestroprogramacompilarasatisfactoriamente.

matchestambiénunaexpresión,loquesignificaquelopodemosusarenelladoderechodeunletodirectamenteendondeunaexpresiónseausada:

ElLenguajedeProgramacionRust

201Match

letx=5;

letnumber=matchx{

1=>"uno",

2=>"dos",

3=>"tres",

4=>"cuatro",

5=>"cinco",

_=>"otracosa",

};

Algunasvecesesunabuenaformadeconvertiralgodeuntipoaotro.

Haciendo matchenenumsOtrousoimportantedelapalabrareservadamatchesparaprocesarlasposiblesvariantesdeunaenum:

enumMensaje{

Salir,

CambiarColor(i32,i32,i32),

Mover{x:i32,y:i32},

Escribir(String),

}

fnsalir(){/*...*/}

fncambiar_color(r:i32,g:i32,b:i32){/*...*/}

fnmover_cursor(x:i32,y:i32){/*...*/}

fnprocesar_mensaje(msj:Mensaje){

matchmsj{

Mensaje::Salir=>salir(),

Mensaje::CambiarColor(r,g,b)=>cambiar_color(r,g,b),

Mensaje::Mover{x:x,y:y}=>mover_cursor(x,y),

Mensaje::Escribir(s)=>println!("{}",s),

};

}

Otravez,elcompiladordeRustchequeaagotamiento,demandandoquetengasunbrazoparacadavariantedelaenumeración.Sidejasunoporfuera,Rustgeneraraunerrorentiempodecompilación,amenosqueuses_.

Adiferenciadelosusospreviosdematch,nopuedesusardelasentenciaifparahaceresto.Puedeshacerusodeunasentenciaifletquepuedeservistacomounaformaabreviadadematch.

ElLenguajedeProgramacionRust

202Match

%Patrones

LospatronessonbastantecomunesenRust.Losusamosenenlacesavariable,sentenciasmatch,yotroscasos.Embarquemonosenuntourtorbellinoportodaslascosasquelospatronessoncapacesdehacer!

Unrepasorápido:puedesprobarpatronescontraliteralesdirectamente,y_actúacomouncasocualquiera:

letx=1;

matchx{

1=>println!("uno"),

2=>println!("dos"),

3=>println!("tres"),

_=>println!("cualquiera"),

}

Imprimeuno.

MultiplespatronesPuedesprobarmultiplespatronescon|:

letx=1;

matchx{

1|2=>println!("unoodos"),

3=>println!("tres"),

_=>println!("cualquiera"),

}

Loanteriorimprimeunoodos.

DestructuracionSiposeesuntipodedatoscompuesto,comounstruct,puedesdestructurarlodentrodeunpatron:

ElLenguajedeProgramacionRust

203Patrones

structPunto{

x:i32,

y:i32,

}

letorigen=Punto{x:0,y:0};

matchorigen{

Punto{x,y}=>println!("({},{})",x,y),

}

Puedesusar:paradarleunnombrediferenteaunvalor.

structPunto{

x:i32,

y:i32,

}

letorigen=Punto{x:0,y:0};

matchorigen{

Punto{x:x1,y:y1}=>println!("({},{})",x1,y1),

}

Sisolonosimportanalgunosvalores,notenemosquedarlenombresatodos:

structPunto{

x:i32,

y:i32,

}

letorigen=Punto{x:0,y:0};

matchorigen{

Punto{x,..}=>println!("xes{}",x),

}

Estoimprimexes0.

Puedeshacerestetipodepruebasencualquiermiembro,nosoloelprimero:

ElLenguajedeProgramacionRust

204Patrones

structPunto{

x:i32,

y:i32,

}

letorigen=Punto{x:0,y:0};

matchorigen{

Punto{y,..}=>println!("yes{}",y),

}

Loanteriorimprimeyes0.

Estecomportamientode‘destructuracion’funcionaencualquiertipodedatoscompuesto,comotuplasoenums.

IgnorandoenlacesavariablesPuedesusar_enunpatronparaignorartantoeltipocomoelvalor.

Porejemplo,heaquíunmatchcontraunResult<T,E>:

#letalgun_valor:Result<i32,&'staticstr>=Err("Hubounerror");

matchalgun_valor{

Ok(valor)=>println!("valorobtenido:{}",valor),

Err(_)=>println!("haocurridounerror"),

}

Enelprimerbrazo,enlazamoselvalordentrodelavarianteOkalavariablevalor.PeroenelbrazoErrusamos_paraignorarelerrorespecifico,ysoloimprimirunmensajedeerrorgeneral.

_esvalidoencualquierpatronquecreeunenlaceavariable.Tambiénpuedeserutilparaignorarporcionesdeunaestructuramasgrande:

fncoordenada()->(i32,i32,i32){

//generaryretornaralgúntipodetupladetreselementos

#(1,2,3)

}

let(x,_,z)=coordenada();

Aquí,asociamosamboselprimeryultimoelementodelatuplaaxyzrespectivamente,ignorandoelelementodelamitad.

ElLenguajedeProgramacionRust

205Patrones

Similarmente,puedesusar..enunpatrónparaignorarmultiplesvalores.

enumTuplaOpcional{

Valor(i32,i32,i32),

Faltante,

}

letx=TuplaOpcional::Valor(5,-2,3);

matchx{

TuplaOpcional::Valor(..)=>println!("Tuplaobtenida!"),

TuplaOpcional::Faltante=>println!("Sinsuerte."),

}

EstoimprimeTuplaobtenida!.

refyrefmutSideseasobtenerunareferencia,debesusarlapalabrareservadaref:

letx=5;

matchx{

refr=>println!("Referenciaa{}obtenida",r),

}

ImprimeReferenciaa5obtenida.

Acá,lardentrodelmatchposeeeltipo&i32.Enotraspalabraslapalabrareservadarefcreaunareferencia,paraserusadadentrodelpatrón.Sinecesitasunareferenciamutablerefmutfuncionaradelamismamanera:

letmutx=5;

matchx{

refmutrm=>println!("Referenciamutablea{}obtenida",rm),

}

RangosPuedesprobarunrangodevalorscon...:

ElLenguajedeProgramacionRust

206Patrones

letx=1;

matchx{

1...5=>println!("unoalcinco"),

_=>println!("cualquiercosa"),

}

Estoimprimeunoalcinco.

Losrangossonusadosmayormenteconenterosycharss:

letx='';

matchx{

'a'...'j'=>println!("letratemprana"),

'k'...'z'=>println!("letratardia"),

_=>println!("algomas"),

}

Thisprintsalgomas.

EnlacesavariablePuedesasociarvaloresanombrescon@:

letx=1;

matchx{

e@1...5=>println!("valorderango{}obtenido",e),

_=>println!("loquesea"),

}

Thisprintsvalorderango1obtenido.Loanterioresutilcuandodeseeshacerunmatchcomplicadoaunapartedeunaestructuradedatos:

ElLenguajedeProgramacionRust

207Patrones

#[derive(Debug)]

structPersona{

nombre:Option<String>,

}

letnombre="Steve".to_string();

letmutx:Option<Persona>=Some(Persona{nombre:Some(nombre)});

matchx{

Some(Persona{nombre:refa@Some(_),..})=>println!("{:?}",a),

_=>{}

}

DichocódigoimprimeSome("Steve"):hemosasociadoelnombreinternoaa.

Siusas@con|,necesitasasegurartedequeelnombreseaasociadoencadapartedelpatron:

letx=5;

matchx{

e@1...5|e@8...10=>println!("valorderango{}obtenido",e),

_=>println!("loquesea"),

}

GuardiasPuedesintroducirguardiasmatch(‘matchguards’)conif:

enumEnteroOpcional{

Valor(i32),

Faltante,

}

letx=EnteroOpcional::Value(5);

matchx{

EnteroOpcional::Valor(i)ifi>5=>println!("Enteromayoracincoobtenido!"),

EnteroOpcional::Valor(..)=>println!("Enteroobtenido!"),

EnteroOpcional::Faltante=>println!("Sinsuerte."),

}

EstoimprimeEnteroobtenido!".

Siestasusandoifconmultiplespatrones,elifaplicaaamboslados:

ElLenguajedeProgramacionRust

208Patrones

letx=4;

lety=false;

matchx{

4|5ify=>println!("si"),

_=>println!("no"),

}

Loanteriorimprimeno,debidoaqueelifaplicaael4|5completo,ynosoloal5.Enotraspalabras,laprecedenciadelifsecomportadelasiguientemanera:

(4|5)ify=>...

ynoasí:

4|(5ify)=>...

MezclayMatchUff!Esofueunmontóndeformasdiferentesparaprobarcosas,ytodaspuedensermezcladasyprobadas,dependiendodelosqueestéshaciendo:

matchx{

Foo{x:Some(refnombre),y:None}=>...

}

Lospatronessonmuypoderosos.Hazbuenusodeellos.

ElLenguajedeProgramacionRust

209Patrones

%SintaxisdeMétodos

Lasfuncionessongeniales,perosideseasllamarbastantesenalgunadata,puedetornarseincomodo.Consideraestecódigo:

baz(bar(foo));

Pudiéramosleerestodeizquierdaaderecha,veríamosentonces‘bazbarfoo’.Peroesenoeselordenenelcuallasfuncionessonllamadas,elordendehecho,esdeadentrohaciaafuera:‘foobarbaz’.Noseriaexcelentesipudiéramoshacerlo:

foo.bar().baz();

Porsuerte,comohabráspodidodeducirdelapreguntaanterior,porsupuestoquepodemos!Rustproveelahabilidaddeusarladenominada‘sintaxisdellamadaamétodos’(‘methodcallsyntax’)atravésdelapalabrareservadaimpl.

LlamadasamétodosAsífuncionan:

structCirculo{

x:f64,

y:f64,

radio:f64,

}

implCirculo{

fnarea(&self)->f64{

std::f64::consts::PI*(self.radio*self.radio)

}

}

fnmain(){

letc=Circulo{x:0.0,y:0.0,radio:2.0};

println!("{}",c.area());

}

Loanteriorimprimira12.566371.

Hemosconstruidounastructrepresentandoauncirculo.Despuésescribimosunbloqueimplydentrodeel,definimosunmétodo,area.

ElLenguajedeProgramacionRust

210SintaxisdeMétodos

Losmétodosrecibenunprimerparámetroespecial,delcualexistentresvariantes:self,&self,y&mutself.Puedespensaracercadeesteprimerparámetrocomoelfooenfoo.bar().Lastresvariantescorrespondenalostrestiposdecosasquefoopodríaser:selfsiessolounvalorenlapila,&selfsiesunareferenciay&mutselfsiesunareferenciamutable.Debidoaqueproporcionamoselparámetro&selfaarea,podemosusarlojustocomocualquierotro.ComoconocemosqueesunCirculo,podemosaccederaradiocomoloharíamosconcualquierotrastruct.

Deberíamosusarpordefecto&self,asícomopreferirtambiénelpréstamoporencimadelatomadepertenenciayrecibirreferenciasinmutablesenvezdemutables,enloposible.Heaquíunejemplodelastresvariantes:

structCirculo{

x:f64,

y:f64,

radio:f64,

}

implCircle{

fnreferencia(&self){

println!("recibiendoaselfcomounareferencia!");

}

fnreferencia_mutable(&mutself){

println!("trecibiendoaselfcomounareferenciamutable!");

}

fntoma_pertenecia(self){

println!("tomandopertenenciadeself!");

}

}

LlamadasamétodosencadenaEntonces,ahorasabemoscomollamaraunmétodo,comofoo.bar().Peroquehayacercadenuestroejemplooriginal,foo.bar().baz()?Sedenomina‘encadenamientodemétodos’(‘methodchaining’).Veamosunejemplo:

ElLenguajedeProgramacionRust

211SintaxisdeMétodos

structCirculo{

x:f64,

y:f64,

radio:f64,

}

implCirculo{

fnarea(&self)->f64{

std::f64::consts::PI*(self.radio*self.radio)

}

fnagrandar(&self,incremento:f64)->Circulo{

Circulo{x:self.x,y:self.y,radio:self.radio+incremento}

}

}

fnmain(){

letc=Circulo{x:0.0,y:0.0,radio:2.0};

println!("{}",c.area());

letd=c.agrandar(2.0).area();

println!("{}",d);

}

Observaelvalorderetorno:

#structCirculo;

#implCirculo{

fnagrandar(&self,incremento:f64)->Circulo{

#Circulo}}

SolodecimosqueretornamosunCirculo.Conestemétodo,podemosagrandarunnuevoCirculoauntamañoarbitrario.

FuncionesasociadasPuedestambiéndefinirfuncionesasociadasquenotomenelparámetroself.HeaquíunpatronmuycomúnencódigoRust:

ElLenguajedeProgramacionRust

212SintaxisdeMétodos

structCirculo{

x:f64,

y:f64,

radio:f64,

}

implCirculo{

fnnew(x:f64,y:f64,radio:f64)->Circulo{

Circulo{

x:x,

y:y,

radio:radio,

}

}

}

fnmain(){

letc=Circulo::new(0.0,0.0,2.0);

}

Esta‘funciónasociada’construyeunnuevoCirculo.NotaquelasfuncionesasociadassonllamadasconlasintaxisStruct::funcion(),enlugarderef.metodo().Otroslenguajesllamanalasfuncionesasociadas‘métodosestáticos’

ElpatrónConstructor(Builder)DigamosquequeremosquenuestrosusuariospuedancrearCirculos,perosololespermitiremosdarvaloresalaspropiedadesqueseanrelevantesparaellos.Delocontrario,losatributosxyyserán0.0,yelradiosera1.0.Rustnoposeesobrecargademétodos,argumentosconnombreoargumentosvariables.Seempleaelpatronbuilderensulugar.Dichopatronluceasí:

structCirculo{

x:f64,

y:f64,

radio:f64,

}

implCirculo{

fnarea(&self)->f64{

std::f64::consts::PI*(self.radio*self.radio)

}

}

structConstructorCirculo{

x:f64,

ElLenguajedeProgramacionRust

213SintaxisdeMétodos

y:f64,

radio:f64,

}

implConstructorCirculo{

fnnew()->ConstructorCirculo{

ConstructorCirculo{x:0.0,y:0.0,radio:1.0,}

}

fnx(&mutself,coordenada:f64)->&mutConstructorCirculo{

self.x=coordenada;

self

}

fny(&mutself,coordenada:f64)->&mutConstructorCirculo{

self.y=coordenada;

self

}

fnradio(&mutself,radio:f64)->&mutConstructorCirculo{

self.radio=radio;

self

}

fnfinalizar(&self)->Circulo{

Circulo{x:self.x,y:self.y,radio:self.radio}

}

}

fnmain(){

letc=ConstructorCirculo::new()

.x(1.0)

.y(2.0)

.radio(2.0)

.finalizar();

println!("area:{}",c.area());

println!("x:{}",c.x);

println!("y:{}",c.y);

}

Loquehemoshechoescrearotrastruct,ConstructorCirculo.Hemosdefinidonuestrosmétodosconstructoresenella.Tambiénhemosdefinidonuestrométodoarea()enCirculo.HemosagregadootrométodoenConstructorCirculo:finalizar().EstemétodocreanuestroCirculodesdeelconstructor.Ahora,hemosusadoelsistemadetiposparahacervalernuestrosintereses:podemosusarlosmétodosenConstructorCirculoparacrearCirculosenlaformaquedecidamos.

ElLenguajedeProgramacionRust

214SintaxisdeMétodos

%Vectores

Un‘vector’esunarreglodinámico,implementadocomoeltipodelabibliotecaestándarVec<T>.LaTsignificaquepodemostenervectoresdecualquiertipo(echaunvistazoalcapitulode[genéricos][generics]paramasinformación).Losvectoressiemprealojansusdatosenelmontículo.Puedescrearvectoresconlamacrovec!:

letv=vec![1,2,3,4,5];//v:Vec<i32>

(Notaqueadiferenciadelamacroprintln!quehemosusadoenelpasado,usamoscorchetes[]conlamacrovec!.Rusttepermiteusarcualquieraencualquiersituación,enestaoportunidadesporpuraconvención)

Existeunaformaalternativadevec!pararepetirunvalorinicial:

letv=vec![0;10];//diezceros

AccediendoaelementosParaobtenerelvalorenunindiceenparticulardelvector,usamos[]s:

letv=vec![1,2,3,4,5];

println!("Eltercerelementodeves{}",v[2]);

Losindicescomienzandesde0,entonces,eltercerelementoesv[2].

IterandoUnavezposeasunvector,puedesiteraratravésdesuselementosconfor.Haytresversiones:

ElLenguajedeProgramacionRust

215Vectores

letmutv=vec![1,2,3,4,5];

foriin&v{

println!("Unareferenciaa{}",i);

}

foriin&mutv{

println!("Unareferenciamutablea{}",i);

}

foriinv{

println!("Tomandopertenenciadelvectorysuelemento{}",i);

}

Losvectoresposeenmuchosotrosmétodosútiles,acercadeloscualespuedesleerensudocumentation

ElLenguajedeProgramacionRust

216Vectores

%CadenasdeCaracteres

Lascadenasdecaracteressonunconceptoimportanteadominarparacualquierprogramador.ElsistemademanejodecadenasdecaracteresenRustesunpocodistintoaldeotroslenguajes,debidoasufocoenprogramacióndesistemas.Siemprequeposeasunaestructuradedatosdetamañovariable,lascosaspuedenponerseunpocodifíciles,ylascadenasdecaracteressonunaestructuradedatosquepuedevariarentamaño.Dichoesto,lascadenasdecaracteresdeRusttambiénfuncionandemaneradiferentequeenalgunosotroslenguajesdeprogramacióndesistemas,comoC.

Entremosenlosdetalles.Una‘cadenadecaracteres’(‘string’)esunasecuenciadevaloresescalaresUnicodecodificadacomounflujodebytesUTF-8.TodaslascadenasdecaracteresestángarantizadasaserunacodificaciónvalidadesecuenciasUTF-8.Adicionalmente,yadiferenciadeotroslenguajesdesistemas,lascadenasdecaracteresnosonterminadasennullypuedencontenerbytesnull.

Rustposeedostiposprincipalesdecadenasdecaracteres:&stryString.Hablemosprimeroacercade&str.Estossondenominados‘pedazosdecadenasdecaracteres’(‘stringslices’).LosliteralesStringsondeltipo&'staticstr`:

letsaludo="Hola.";//saludo:&'staticstr

Estacadenadecaracteresesasignadaestáticamente,significandoqueesalmacenadadentrodenuestroprogramacompilado,yexisteporladuracióncompletadesuejecución.Elenlacesaludoesunareferenciaaunacadenaasignadaestéticamente.Lospedazosdecadenasdecaracteresposeenuntamañofijo,ynopuedensermutados.

Porotrolado,unString,esunacadenadecaracteresasignadadesdeelmontículo.Dichacadenapuedecrecer,ytambiénestagarantizadaserUTF-8.LosStringsoncreadoscomúnmenteatravésdelaconversióndeunpedazodecadenadecarácterusandoelmétodoto_string.

letmuts="Hola".to_string();//muts:String

println!("{}",s);

s.push_str(",mundo.");

println!("{}",s);

LosStringsharancoercionaun&strconun&:

ElLenguajedeProgramacionRust

217CadenasdeCaracteres

fnrecibe_pedazo(pedazo:&str){

println!("Recibí:{}",pedazo);

}

fnmain(){

lets="Hola".to_string();

recibe_pedazo(&s);

}

Estacoerciónnoocurreparalasfuncionesqueaceptanunodelostraits&str’senlugarde&str.Porejemplo,TcpStream::connectposeeunparámetrodetipoToSocketAddrs.Un&strestabienperounStringdebeserexplícitamenteconvertidousando&*.

usestd::net::TcpStream;

TcpStream::connect("192.168.0.1:3000");//parametro&str

letcadena_direccion="192.168.0.1:3000".to_string();

TcpStream::connect(&*cadena_direccion);//convirtiendocadena_direcciona&str

VerunStringcomoun&stresbarato,peroconvertirel&straunStringinvolucraasignacióndememoria.Nohayrazónparahaceresoamenosqueseanecesario!

IndexadoDebidoaquelascadenasdecaracteressonUTF-8validos,nosoportanindexado:

lets="hola";

println!("Laprimeraletradeses{}",s[0]);//ERROR!!!

Usualmente,elaccesoaunvectorcon[]esmuyrápido.Pero,puestoaquecadacaráctercodificadoenunacadenaUTF-8puedetenermultiplesbytes,debesrecorrertodalacadenaparaencontrarlanᵗʰletradeunacadena.Estaesunaoperaciónsignificativamentemascara,ynoqueremosgenerarconfusiones.Incluso,‘letra’noesexactamentealgodefinidoenUnicode.Podemosescogerveraunacadenadecaracterescomobytesindividuales,ocomocodepoints:

ElLenguajedeProgramacionRust

218CadenasdeCaracteres

lethachiko="忠犬ハチ公";

forbinhachiko.as_bytes(){

print!("{},",b);

}

println!("");

forcinhachiko.chars(){

print!("{},",c);

}

println!("");

Loanteriorimprime:

229,191,160,231,138,172,227,131,143,227,131,129,229,133,172,

忠,犬,ハ,チ,公,

Comopuedesver,haymasbytesquecaracteres(chars).

Puedesobteneralgosimilaraunindicedeestaforma:

#lethachiko="忠犬ハチ公";

letperro=hachiko.chars().nth(1);//algocomohachiko[1]

Estoenfatizaquetenemosquecaminardesdeelprincipiodelalistadechars.

Cortado(Slicing)Puedesobtenerunpedazodeunacadenadecaracteresconlasintaxisdecortado:

letperro="hachiko";

lethachi=&perro[0..5];

Peronotaqueestossondesplazamientosdebyte,nodesplazamientosdecharacter.Entonces,losiguientefallaraentiempodeejecución:

letperro="忠犬ハチ公";

lethachi=&perro[0..2];

conesteerror:

ElLenguajedeProgramacionRust

219CadenasdeCaracteres

thread'

'panickedat'index0and/or2in`忠犬ハチ公`donotlieon

characterboundary'

ConcatenaciónSiposeesunString,puedesconcatenarleun&stralfinal:

lethola="Hola".to_string();

letmundo="mundo!";

lethola_mundo=hola+mundo;

PerositienesdosStrings,necesitasun&:

lethola="Hola".to_string();

letmundo="mundo!".to_string();

lethola_mundo=hola+&world;

Estoesporque&Stringpuedehacercoercionautomáticaaun&str.Estacaracterísticaesdenominada‘coercionesDeref’.

ElLenguajedeProgramacionRust

220CadenasdeCaracteres

%Genéricos

Algunasveces,cuandoseescribeunafunciónounaestructuradedatos,podríamosdesearqueestapudiesefuncionarconmultiplestiposdeargumentos.EnRustpodemoslograrestoatravésdelosgenéricos.Losgenéricossonllamados‘polimorfismoparametrico’enlateoríadetipos,loquesignificaquesontiposofuncionesqueposeenmultiplesformas(‘poly’demultiple,‘morph’deforma)sobreundeterminadoparámetro(‘parametrico’).

Decualquiermodo,suficienteacercadeteoríadetipos,veamosunpocodecódigogenérico.LabibliotecaestándardeRustproveeuntipo,Option<T>,elcualesgenérico:

enumOption<T>{

Some(T),

None,

}

Laparte<T>,lacualhasvistounascuantasvecesanteriormente,indicaqueesteesuntipodedatosgenérico.Dentrodeladeclaracióndenuestroenum,dondeseaqueveamosunaTsustituimosesetipoporelmismotipousadoenelgenérico.HeaquíunejemplodelusodeOption<T>,conalgunosanotacionesdetipoextra:

letx:Option<i32>=Some(5);

Enladeclaracióndeltipo,decimosOption<i32>.NotacuansimilarestoluceestoaOption<T>.Entonces,enesteOption,Tposeeelvalordei32.Enelladoderechodelbinding,hacemosunSome(T),endondeTes5.Debidoaque5esuni32,ambosladoscoinciden,yRustesfeliz.Denohabercoincidido,hubiésemosobtenidounerror:

letx:Option=Some(5);

//error:mismatchedtypes:expected`core::option::Option`,

//found`core::option::Option<_>`(expectedf64butfoundintegralvariable)

EsonosignificaquenopodamoscrearOption<T>squecontenganunf64.Simplementedebencoincidir:

letx:Option<i32>=Some(5);

lety:Option<f64>=Some(5.0f64);

Muybueno.Unadefinición,multiplesusos.

Losgenéricosnodebennecesariamentesergenéricossobreunsolotipo.ConsideraotrotiposimilarenlabibliotecaestándardeRust,Result<T,E>:

ElLenguajedeProgramacionRust

221Genéricos

enumResult<T,E>{

Ok(T),

Err(E),

}

Estetipoesgenéricosobredostipos:TyE.Porcierto,lasletrasmayusculaspuedensercualquiera.PudimoshaberdefinidoResult<T,E>como:

enumResult<A,Z>{

Ok(A),

Err(Z),

}

dehaberquerido.LaconvencióndicequeelprimerparametrogenericodebeserT,de‘tipo’,yqueusemosEpara‘error’.ARust,sinembargonoleimporta.

EltipoResult<T,E>seusapararetornarelresultadodeunacomputación,conlaposibilidadderetornarunerrorencasodequedichacomputaciónnohayasidoexitosa.

FuncionesgenéricasPodemosescribirfuncionesquetomentiposgenéricosconunasintaxissimilar:

fnrecibe_cualquier_cosa<T>(x:T){

//haceralgoconx

}

Lasintaxisposeedospartes:el<T>dice“estafunciónesgenericasobreuntipo,T”,ylapartex:Tdice“xposeeeltipoT.”

Multiplesargumentospuedentenerelmismotipo:

fnrecibe_dos_cosas_del_mismo_tipo<T>(x:T,y:T){

//...

}

Pudimoshaberescritounaversionquerecibemultiplestipos:

fnrecibe_dos_cosas_de_distintos_tipos<T,U>(x:T,y:U){

//...

}

ElLenguajedeProgramacionRust

222Genéricos

StructsgenericosTambiénpuedesalmacenaruntipogenéricoenunaestructura:

structPunto<T>{

x:T,

y:T,

}

letorigen_entero=Punto{x:0,y:0};

letorigen_flotante=Punto{x:0.0,y:0.0};

Similarmentealasfunciones,lasección<T>esendondedeclaramoslosparámetrosgenéricos,despuésdeellohacemosusodex:Tenladeclaracióndeltipo,también.

Cuandodeseamosagregarunaimplementaciónparalaestructuragenérica,simplementedeclaraselparámetrodetipodespuésdeimpl:

#structPunto<T>{

#x:T,

#y:T,

#}

#

impl<T>Punto<T>{

fnintercambiar(&mutself){

std::mem::swap(&mutself.x,&mutself.y);

}

}

Hastaahorasolohasvistogenéricosqueaceptanabsolutamentecualquiertipo.Estassonútilesenmuchoscasos,yahasvistoOption<T>,ymastardeconocerascontenedoresuniversalescomoVec<T>.Porotrolado,avecesquerrásintercambiaresaflexibilidadpormayorpoderexpresivo.Leeacercadelimitestraitparaverporqueycomo.

ElLenguajedeProgramacionRust

223Genéricos

%Traits

UntraitesunafacilidaddellenguajequeleindicacompiladordeRustacercadelafuncionalidadqueuntipodebeproveer.

Recuerdaslapalabrareservadaimpl?,usadaparallamaraunafunciónconlasintaxisdemétodos?

structCirculo{

x:f64,

y:f64,

radio:f64,

}

implCirculo{

fnarea(&self)->f64{

std::f64::consts::PI*(self.radio*self.radio)

}

}

Lostraitssonsimilares,exceptoquedefinimosuntraitconsololafirmademétodoyluegoimplementamoseltraitparalaestructura.Así:

structCirculo{

x:f64,

y:f64,

radio:f64,

}

traitTieneArea{

fnarea(&self)->f64;

}

implTieneAreaforCirculo{

fnarea(&self)->f64{

std::f64::consts::PI*(self.radio*self.radio)

}

}

Comopuedesver,elbloquetraitlucemuysimilaraelbloqueimpl,peronodefinimosunbloque,sololafirmadetipos.Cuandoimplementamosuntrait,usamosimplTraitforItem,envezdesoloimplItem.

Limitestraitparafuncionesgenéricas

ElLenguajedeProgramacionRust

224Traits

Lostraitssonútilesporquepermitenauntipohacerciertaspromesasacercadesucomportamiento.Lafuncionesgenéricaspuedenexplotarestopararestringirlostiposqueaceptan.Consideraestafunción,lacualnocompila:

fnimrimir_area(figura:T){

println!("Estafiguratieneunareade{}",figura.area());

}

Rustsequeja:

error:nomethodnamed`area`foundfortype`T`inthecurrentscope

DebidoaqueTpuedeserdecualquiertipo,nopodemosestarsegurosqueimplementaelmétodoarea.Peropodemosagregaruna‘restriccióndetrait’anuestroTgenérico,asegurándonosdequeloimplemente:

#traitTieneArea{

#fnarea(&self)->f64;

#}

fnimrimir_area<T:TieneArea>(shape:T){

println!("Estafiguratieneunareade{}",figura.area());

}

Lasintaxis<T:TieneArea>setraduceen“cualquiertipoqueimplementeeltraitTieneArea.”.Aconsecuenciadequelostraitsdefinenfirmasdetiposdefunción,podemosestarsegurosquecualquiertipoqueimplementeTieneAreatendráunmétodo.area().

Heaquíunejemploextendidodecomoestofunciona:

ElLenguajedeProgramacionRust

225Traits

traitTieneArea{

fnarea(&self)->f64;

}

structCirculo{

x:f64,

y:f64,

radio:f64,

}

implTieneAreaforCirculo{

fnarea(&self)->f64{

std::f64::consts::PI*(self.radio*self.radio)

}

}

structCuadrado{

x:f64,

y:f64,

lado:f64,

}

implTieneAreaforCuadrado{

fnarea(&self)->f64{

self.lado*self.lado

}

}

fnimrimir_area<T:TieneArea>(figura:T){

println!("Estafiguratieneunareade{}",figura.area());

}

fnmain(){

letc=Circulo{

x:0.0f64,

y:0.0f64,

radio:1.0f64,

};

lets=Cuadrado{

x:0.0f64,

y:0.0f64,

lado:1.0f64,

};

imrimir_area(c);

imrimir_area(s);

}

Esteprogramaproducelasalida:

ElLenguajedeProgramacionRust

226Traits

Estafiguratieneunareade3.141593

Estafiguratieneunareade1

Comopuedesver,imrimir_areaahoraesgenérica,perotambiénaseguraquehallamosproporcionadolostiposcorrectos.Sipasamosuntipoincorrecto:

imrimir_area(5);

Obtenemosunerrorentiempodecompilación:

error:thetrait`TieneArea`isnotimplementedforthetype`_`[E0277]

LimitesdetraitparaestructurasgenericasTusestructurasgenéricaspuedenbeneficiarsetambiéndelasrestriccionesdetrait.Todoloquenecesitasesagregarlarestriccióncuandodeclaraslosparámetrosdetipos.AcontinuaciónunnuevotipoRectangulo<T>ysuoperaciónes_cuadrado:

structRectangulo<T>{

x:T,

y:T,

ancho:T,

altura:T,

}

impl<T:PartialEq>Rectangulo<T>{

fnes_cuadrado(&self)->bool{

self.ancho==self.altura

}

}

fnmain(){

letmutr=Rectangulo{

x:0,

y:0,

ancho:47,

altura:47,

};

assert!(r.es_cuadrado());

r.altura=42;

assert!(!r.es_cuadrado());

}

ElLenguajedeProgramacionRust

227Traits

es_cuadrado()necesitachequearquelosladossoniguales,yparaellolostiposdebenserdeuntipoqueimplementeeltraitcore::cmp::PartialEq:

implRectangulo{...}

Ahora,unrectángulopuedeserdefinidoenfuncióndecualquiertipoquepuedasercomparadoporigualdad.

HemosdefinidounanuevaestructuraRectanguloqueaceptanúmerosdecualquierprecision,objetosdecualquiertiposiempreycuandopuedansercomparadosporigualdad.PodríamoshacerlomismoparanuestrasestructurasTieneArea,CuadradoyCirculo?Si,peroestasnecesitanmultiplicación,yparatrabajarconesonecesitamossabermasdelostraitsdeoperadores.

ReglasparalaimplementacióndetraitsHastaahora,solohemosagregadoimplementacionesdetraitsaestructuras,peropuedesimplementarcualquiertraitparacualquiertipo.Técnicamente,podriamosimplementarTieneAreaparai32:

traitTieneArea{

fnarea(&self)->f64;

}

implTieneAreafori32{

fnarea(&self)->f64{

println!("estoestonto");

*selfasf64

}

}

5.area();

Seconsiderapobreestiloimplementarmétodosenesostiposprimitivos,auncuandoesposible.

Estopuedelucircomoelviejooeste,perohaydosrestriccionesacercadelaimplementacióndetraitsqueprevienenquelascosassesalgandecontrol.Laprimeraesquesieltraitnoestadefinidoentuámbito,noaplica.Heaquíunejemplo:labibliotecaestándarproveeuntraitWritequeagregafuncionalidadextraalosFiles,posibilitandolaE/Sdearchivos.Pordefecto,unFilenotendríasusmétodos:

ElLenguajedeProgramacionRust

228Traits

letmutf=std::fs::File::open("foo.txt").ok().expect("Nosepudoabrirfoo.txt");

letbuf=b"cualquiercosa";//literaldecadenadebytes.buf:&[u8;8]

letresultado=f.write(buf);

#resultado.unwrap();//ignorarelerror

Heaquielerror:

error:type`std::fs::File`doesnotimplementanymethodinscopenamed`write`

letresultado=f.write(buf);

^~~~~~~~~~

NecesitamosprimerohacerusedeltraitWrite:

usestd::io::Write;

letmutf=std::fs::File::open("foo.txt").ok().expect("Nosepudoabrirfoo.txt");

letbuf=b"cualquiercosa";

letresultado=f.write(buf);

#resultado.unwrap();//ignorarelerror

Loanteriorcompilarasinerrores.

Estosignificaqueinclusosialguienhacealgomalocomoagregarmétodosai32,noteafectara,amenosquehagasusedeesetrait.

Hayunarestricciónmasacercadelaimplementacióndetraits:unodelosdosbienseaeltraitoeltipoparaelcualestasescribiendolaimpl,debeserdefinidoporti.Entonces,podríamosimplementareltraitTieneAreaparaeltipoi32,puestoqueTieneAreaestaennuestrocódigo.PerosiintentáramosimplementarToString,untraitproporcionadoporRustparai32,nopodríamos,debidoaquenieltraitoeltipoestánennuestrocódigo.

Unaultimacosaacercadelostraits:lasfuncionesgenéricasconunlimitedetraitusan‘monomorfizacion’(‘monomorphization’)(mono:uno,morfos:forma),yporellosondespachadasestáticamente.Quesignificaesto?Echaunvistazoaelcapituloacercadeobjetostraitparamasdetalles.

MultipleslimitesdetraitHasvistoquepuedeslimitarunparámetrodetipogenéricoconuntrait:

ElLenguajedeProgramacionRust

229Traits

fnfoo<T:Clone>(x:T){

x.clone();

}

Sinecesitasmasdeunlimite,puedeshacerusode+:

usestd::fmt::Debug;

fnfoo<T:Clone+Debug>(x:T){

x.clone();

println!("{:?}",x);

}

TnecesitaahoraserambosCloneyDebug.

LaclausulawhereEscribirfuncionesconsolounospocostiposgenéricosyunpequeñonumerodelimitesdetraitnoestanfeo,peroamedidaqueelnumeroseincrementa,lasintaxissevuelveunpocoextraña:

usestd::fmt::Debug;

fnfoo<T:Clone,K:Clone+Debug>(x:T,y:K){

x.clone();

y.clone();

println!("{:?}",y);

}

Elnombredelafunciónestalejosalaizquierda,ylalistadeparámetrosestalejosaladerecha.Loslimitesdetraitseinterponenenlamitad.

Rusttieneunasolución,ysellama‘clausulawhere’:

ElLenguajedeProgramacionRust

230Traits

usestd::fmt::Debug;

fnfoo<T:Clone,K:Clone+Debug>(x:T,y:K){

x.clone();

y.clone();

println!("{:?}",y);

}

fnbar<T,K>(x:T,y:K)whereT:Clone,K:Clone+Debug{

x.clone();

y.clone();

println!("{:?}",y);

}

fnmain(){

foo("Hola","mundo");

bar("Hola","mundo");

}

foo()usalasintaxisdemostradapreviamente,ybar()usaunaclausulawhere.Todoloquenecesitasesdejarloslimitesporfueracuandodefinastusparámetrosdetipoyluegoagregarunwheredespuésdelalistadeparámetros.Paralistasmaslargas,espaciosenblancopuedenseragregados:

usestd::fmt::Debug;

fnbar<T,K>(x:T,y:K)

whereT:Clone,

K:Clone+Debug{

x.clone();

y.clone();

println!("{:?}",y);

}

Dichaflexibilidadpuedeagregarclaridadensituacionescomplejas.

Laclausulawhereestambiénmaspoderosaquelasintaxismassimple.Porejemplo:

ElLenguajedeProgramacionRust

231Traits

traitConvertirA<Salida>{

fnconvertir(&self)->Salida;

}

implConvertirA<i64>fori32{

fnconvertir(&self)->i64{*selfasi64}

}

//puedeserllamadaconT==i32

fnnormal<T:ConvertirA<i64>>(x:&T)->i64{

x.convertir()

}

//puedeserllamadaconT==i64

fninversa<T>()->T

//pestoesuserConvertirAcomosifuera"ConvertirA<i64>"

wherei32:ConvertirA<T>{

42.convertir()

}

Loanteriordemuestraunacaracterísticaadicionaldewhere:permitelimitesenlosqueelladoizquierdoesuntipoarbitrario(i32enestecaso),nosolounsimpleparámetrodetipo(comoT).

MetodospordefectoSiyasabescomounimplementadortípicodefiniráunmétodo,puedespermitiratutraitproporcionarunométodopordefecto:

traitFoo{

fnes_valido(&self)->bool;

fnes_invalido(&self)->bool{!self.es_valido()}

}

LosimplementadoresdeltraitFoonecesitanimplementares_valido(),perononecesitanimplementares_invalido().Loobtendránpordefecto.Tambiénpuedensobreescribirlaimplementaciónpordefectosilodesean:

ElLenguajedeProgramacionRust

232Traits

#traitFoo{

#fnes_valido(&self)->bool;

#

#fnes_invalido(&self)->bool{!self.es_valido()}

#}

structUsaDefault;

implFooforUsaDefault{

fnes_valido(&self)->bool{

println!("UsaDefault.es_validllamada.");

true

}

}

structSobreescribeDefault;

implFooforSobreescribeDefault{

fnes_valido(&self)->bool{

println!("SobreescribeDefault.es_validollamada.");

true

}

fnes_invalido(&self)->bool{

println!("SobreescribeDefault.es_invalidollamada!");

true//estaimplementacionesunaauto-contradiccion!

}

}

letdefault=UsaDefault;

assert!(!default.es_valido());//imprime"UsaDefault.es_validllamada."

letsobre=SobreescribeDefault;

assert!(sobre.is_invalid());//prints"SobreescribeDefault.es_invalidollamada!"

HerenciaAlgunasveces,implementaruntraitrequiereimplementarotro:

traitFoo{

fnfoo(&self);

}

traitFooBar:Foo{

fnfoobar(&self);

}

LosimplementadoresdeFooBardebentambiénimplementarFoo,deestamanera:

ElLenguajedeProgramacionRust

233Traits

#traitFoo{

#fnfoo(&self);

#}

#traitFooBar:Foo{

#fnfoobar(&self);

#}

structBaz;

implFooforBaz{

fnfoo(&self){println!("foo");}

}

implFooBarforBaz{

fnfoobar(&self){println!("foobar");}

}

SiolvidamosimplementarFoo,Rustnoslodira:

error:thetrait`main::Foo`isnotimplementedforthetype`main::Baz`[E0277]

ElLenguajedeProgramacionRust

234Traits

%Drop

Ahoraquehemosdiscutidolostraits,hablemosdeuntraitparticularproporcionadoporlabibliotecaestándardeRust,Drop.EltraitDropproveeunaformadeejecutarcódigocuandounvalorsaledeámbito.Porejemplo:

structTieneDrop;

implDropforTieneDrop{

fndrop(&mutself){

println!("Dropeando!");

}

}

fnmain(){

letx=TieneDrop;

//hacemosalgo

}//xsaledeambitoaqui

Cuandoxsaledeámbitoalfinaldemain(),elcódigodeDropesejecutado.Dropposeeunmétodo,tambiéndenominadodrop().Dichométodotomaunareferenciamutableaself.

Esoestodo!LamecánicadeDropesmuysimple,sinembargo,hayalgunosdetalles.Porejemplo,losvaloressonliberados(dropped)enordenopuestoacomofuerondeclarados.Heaquíotroejemplo:

structExplosivo{

potencia:i32,

}

implDropforExplosivo{

fndrop(&mutself){

println!("BOOMmultiplicadopor{}!!!",self.potencia);

}

}

fnmain(){

letpetardo=Explosivo{potencia:1};

lettnt=Explosivo{potencia:100};

}

Loanteriorimprimira:

ElLenguajedeProgramacionRust

235Drop

BOOMmultiplicadopor100!!!

BOOMmultiplicadopor1!!!

ElTNTsevaprimeroqueelpetardo,debidoquefuecreadodespués.Ultimoenentrar,primeroensalir.

EntoncesparaqueesbuenoDrop?Generalmente,esusadoparalimpiarcualquierrecursoasociadoaunstruct.Porejemplo,eltipoArc<T>esuntipoconconteodereferencias.CuandoDropesllamado,estedecrementaráelcontadordereferencias,ysielnumerototaldereferenciasescero,limpiaráelvalorsubyacente.

ElLenguajedeProgramacionRust

236Drop

%iflet

iflettepermitecombinarifyletparareducirelcostodealgunostiposdecoincidenciadepatrones.

Porejemplo,digamosquetenemosalgúntipodeOption<T>.QueremosllamaraunafunciónsobreelloencasodeserunSome<T>,peronohacernadasiesNone.Seriaalgoasí:

#letoption=Some(5);

#fnfoo(x:i32){}

matchoption{

Some(x)=>{foo(x)},

None=>{},

}

Notenemosqueusarmatchaquí,porejemplo,podríamosusarif:

#letoption=Some(5);

#fnfoo(x:i32){}

ifoption.is_some(){

letx=option.unwrap();

foo(x);

}

Ningunadeesasdosopcionesesparticularmenteatractiva.Podemosusarifletparahacerlomismo,perodemejorforma:

#letoption=Some(5);

#fnfoo(x:i32){}

ifletSome(x)=option{

foo(x);

}

Siunpatrónconcuerdasatisfactoriamente,asociacualquierparteapropiadadelvaloralosidentificadoresenelpatrón,paraluegoevaluarlaexpresión.Sielpatrónnoconcuerda,nopasanada.

Siquisierashaceralgoenelcasodequeelpatrónnoconcuerde,puedesusarelse:

ElLenguajedeProgramacionRust

237iflet

#letoption=Some(5);

#fnfoo(x:i32){}

#fnbar(){}

ifletSome(x)=option{

foo(x);

}else{

bar();

}

whilelet

Demanerasimilar,whileletpuedeserusadocuandodeseesiterarcondicionalmentemientraselvalorconcuerdeconciertopatrón.Conviertecódigocomoeste:

#letoption:Option<i32>=None;

loop{

matchoption{

Some(x)=>println!("{}",x),

_=>break,

}

}

Encódigocomoeste:

#letoption:Option<i32>=None;

whileletSome(x)=option{

println!("{}",x);

}

ElLenguajedeProgramacionRust

238iflet

%ObjetosTrait

Cuandoelcódigoinvolucrapolimorfismo,esnecesariounmecanismoparadeterminarqueversiónespecificadebeserejecutada.Dichomecanismoesdenominadodespacho.Haydosformasmayoresdedespacho:despachoestáticoydespachodinámico.SibienesciertoqueRustprefiereeldespachoestático,tambiénsoportadespachodinámicoatravésdeunmecanismollamado‘objetostrait’.

BasesPorelrestodeestecapitulo,necesitaremosuntraityalgunasimplementaciones.Creemosunosimple,Foo.FooposeeunsolométodoqueretornaunString.

traitFoo{

fnmetodo(&self)->String;

}

Tambiénimplementaremosestetraitparau8yString:

#traitFoo{fnmetodo(&self)->String;}

implFooforu8{

fnmetodo(&self)->String{format!("u8:{}",*self)}

}

implFooforString{

fnmetodo(&self)->String{format!("string:{}",*self)}

}

DespachoestáticoPodemosusareltraitparaefectuardespachoestáticomedianteelusodelimitesdetrait:

ElLenguajedeProgramacionRust

239ObjetosTrait

#traitFoo{fnmetodo(&self)->String;}

#implFooforu8{fnmetodo(&self)->String{format!("u8:{}",*self)}}

#implFooforString{fnmetodo(&self)->String{format!("string:{}",*self)}}

fnhacer_algo<T:Foo>(x:T){

x.metodo();

}

fnmain(){

letx=5u8;

lety="Hola".to_string();

hacer_algo(x);

hacer_algo(y);

}

Rustutiliza‘monomorfizacion’paraeldespachoestáticoenestecódigo.Loquesignificaquecrearaunaversiónespecialdehacer_algo()paraambosu8yString,reemplazandoluegoloslugaresdellamadaconinvocacionesaestasfuncionesespecializadas.Enotraspalabras,Rustgeneraalgoasí:

#traitFoo{fnmetodo(&self)->String;}

#implFooforu8{fnmetodo(&self)->String{format!("u8:{}",*self)}}

#implFooforString{fnmetodo(&self)->String{format!("string:{}",*self)}}

fnhacer_algo_u8(x:u8){

x.metodo();

}

fnhacer_algo_string(x:String){

x.metodo();

}

fnmain(){

letx=5u8;

lety="Hola".to_string();

hacer_algo_u8(x);

hacer_algo_string(y);

}

Loanteriorposeeunagranventaja:eldespachoestáticopermitequelasllamadasafunciónpuedanserinsertadasenlineadebidoaqueelreceptoresconocidoentiempodecompilación,ylainserciónenlineaesclaveparaunabuenaoptimizacion.Eldespachoestáticoesrápido,perovieneconunadesventaja:‘códigoinflado’(‘codebloat’),aconsecuenciadelasrepetidascopiasdelamismafunciónquesoninsertadasenelbinario,unaparacadatipo.

ElLenguajedeProgramacionRust

240ObjetosTrait

Ademas,loscompiladoresnosonperfectosypueden“optimizar”códigohaciendolomaslento.Porejemplo,funcionesinsertadasenlineademaneraansiosainflaranlacachedeinstrucciones(yelcachegobiernatodoanuestroalrededor).Esporelloque#[inline]y#[inline(always)]debenserusadasconcautela,yunarazónporlacualusardespachodinámicoesalgunasvecesmaseficiente.

Sinembargo,elcasocomúnesqueeldespachoestáticoseamaseficiente.Unopuedetenerunadelgadafunciónenvoltoriodespachadademaneraestáticaefectuandodespachodinámico,peronoviceversa,esdecir;lasllamadasestáticassonmasflexibles.Esporestoquelabibliotecaestándarintentaserdespachadadinámicamentesiempreycuandoseaposible.

DespachodinámicoRustproporcionadespachodinámicoatravésdeunafacilidaddenominada‘objetostrait’.Losobjetostrait,como&FoooBox<Foo>,sonvaloresnormalesquealmacenanunvalordecualquiertipoqueimplementaeltraitdeterminado,endondeeltipoprecisosolopuedeserdeterminadoentiempodeejecución.

Unobjetotraitpuedeserobtenidodeunapuntadorauntipoconcretoqueimplementeeltraitconvirtiéndolo(e.j.&xas&Foo)oaplicándolecoercion(e.j.usando&xcomounargumentoaunafunciónquerecibe&Foo).

Esascoercionesyconversionestambiénfuncionanparaapuntadorescomo&mutTa&mutFooyBox<T>aBox<Foo>,peroesoestodohastaelmomento.Lascoercionesyconversionessonidénticas.

Estaoperaciónpuedeservistacomoel‘borrado’delconocimientodelcompiladoracercadeltipoespecificodelapuntador,yesporelloquelosobjetostraitssonavecesreferidoscomoborradodetipos.

Volviendoaelejemploanterior,podemosusarelmismotraitparallevaracabodespachodinámicoconconversióndeobjetostrait:

ElLenguajedeProgramacionRust

241ObjetosTrait

#traitFoo{fnmetodo(&self)->String;}

#implFooforu8{fnmetodo(&self)->String{format!("u8:{}",*self)}}

#implFooforString{fnmetodo(&self)->String{format!("string:{}",*self)}}

fnhacer_algo(x:&Foo){

x.metodo();

}

fnmain(){

letx=5u8;

hacer_algo(&xas&Foo);

}

oatravesdelacoercion:

#traitFoo{fnmetodo(&self)->String;}

#implFooforu8{fnmetodo(&self)->String{format!("u8:{}",*self)}}

#implFooforString{fnmetodo(&self)->String{format!("string:{}",*self)}}

fnhacer_algo(x:&Foo){

x.method();

}

fnmain(){

letx="Hola".to_string();

hacer_algo(&x);

}

UnafunciónquerecibeunobjetotraitnoesespecializadaparacadaunodelostiposqueimplementanFoo:solounacopiaesgenerada,resultandoalgunasveces(peronosiempre)enmenosinflacióndecódigo.Sinembargo,eldespachodinámicovieneconelcostoderequerirlasllamadasmaslentasafuncionesvirtuales,efectivamenteinhibiendocualquierposibilidaddeinserciónenlineaylasoptimizacionesrelacionadas.

Porqueapuntadores?

Rust,adiferenciademuchoslenguajesadministrados,nocolocacosasdetrásdeapuntadorespordefecto,loquesetraduceenquelostipostengandiferentestamaños.Conocereltamañodeunvalorentiempodecompilaciónesimportanteparacosascomo:pasarlocomoargumentoaunafunción,moverloenlapilayasignarle(ydeasignarle)espacioenelmontículoparasualmacenamiento.

ParaFoo,necesitaríamostenerunvalorquepodríasermaspequeñoqueunString(24bytes)ounu8(1byte),asícomocualquierotrotipoquepuedaimplementarFooencratesdependientes(cualquiernumerodebytes).Nohayformadegarantizarqueeste

ElLenguajedeProgramacionRust

242ObjetosTrait

ultimocasopuedafuncionarsilosvaloresnosonalmacenadosenunapuntador,puestoqueesosotrostipospuedenserdetamañoarbitrario.

Colocarelvalordetrásdeunapuntadorsignificaqueeltamañodelvalornoesrelevantecuandoestemoslanzandounobjetotraitporlosalrededores,soloeltamañodelapuntadorensimismo.

Representación

Losmétodosdeltraitpuedenserllamadosenunobjetotraitatravésdeunregistrodeapuntadoresafuncióntradicionalmentellamado‘vtable’(creadoyadministradoporelcompilador).

Losobjetostraitsonsimplesycomplejosalmismotiempo:surepresentationydistribuciónesbastantedirecta,peroexistenalgunosmensajesdeerrorunpocorarosyalgunoscomportamientossorpresivospordescubrir.

Comencemosporlomassimple,larepresentacióndeunobjetotraitentiempodeejecución.Elmodulostd::rawcontienestructscondistribucionesquesonigualdecomplicadasquelasdelostiposintegrados,incluyendolosobjetostrait:

#modfoo{

pubstructTraitObject{

pubdata:*mut(),

pubvtable:*mut(),

}

#}

Esoes,untraitobjectcomo&Fooconsistedeunapuntador‘data’yunapuntador‘vtable’.

Elapuntadordataapuntahacialosdatos(deuntipodesconocidoT)queguardaelobjetotrait,yelvtablepointerapuntaalavtable(tabledemetodosvirtuales’)(‘virtualmethodtable’)correspondientealaimplementacióndeFooparaT.

Unavtableesesencialmenteunastructdeapuntadoresafunción,apuntandoalsegmentoconcretodecódigomaquinaparacadaimplementacióndemetodo.Unallamadaametodocomoobjeto_trait.metodo()retornaraelapuntadorcorrectodesdelavtableyluegoharáunallamadadinámicadeeste.Porejemplo:

ElLenguajedeProgramacionRust

243ObjetosTrait

structFooVtable{

destructor:fn(*mut()),

tamano:usize,

alineacion:usize,

metodo:fn(*const())->String,

}

//u8:

fnllamar_metodo_en_u8(x:*const())->String{

//elcompiladorgarantizaqueestafunciónsoloseallamada

//con`x`apuntandoaunu8

letbyte:&u8=unsafe{&*(xas*constu8)};

byte.metodo()

}

staticFoo_vtable_para_u8:FooVtable=FooVtable{

destructor:/*magiadelcompilador*/,

tamano:1,

alineacion:1,

//conversionaaunapuntadorafunción

metodo:llamar_metodo_en_u8asfn(*const())->String,

};

//String:

fnllamar_metodo_en_String(x:*const())->String{

//elcompiladorgarantizaqueestafunciónsoloseallamada

//con`x`apuntandoaunString

letstring:&String=unsafe{&*(xas*constString)};

string.metodo()

}

staticFoo_vtable_para_String:FooVtable=FooVtable{

destructor:/*magiadelcompilador*/,

//valoresparaunacomputadorade64bits,dividelosporlamitadparaunade32

tamano:24,

alineacion:8,

metodo:llamar_metodo_en_Stringasfn(*const())->String,

};

Elcampodestructorencadavtableapuntaaunafunciónquelimpiaratodoslosrecursosdeltipodelavtable:parau8estrivial,peroparaStringliberaramemoria.EstoesnecesarioparaadueñarsedeobjetostraitcomoBox<Foo>,quenecesitanlimpiaramboslaasignaciónBoxasícomoeltipointernocuandosalgandeámbito.Loscampostamanoy

ElLenguajedeProgramacionRust

244ObjetosTrait

alineacionalmacenaneltamañodeltipoborrado,ysusrequerimientosdealineación;estossonesencialmentenousadosporelmomentopuestoquelainformaciónesembebidaeneldestructor,peroserausadaenelfuturo,amedidaquelosobjetostraitseanprogresivamentehechosmasflexibles.

SupongamosquetenemosalgunosvaloresqueimplementenFoo.LaformaexplicitadeconstrucciónyusodeobjetostraitFoopuedelucirunpococomo(ignorandolasincongruenciasentretipos:sonapuntadoresdecualquiermanera):

leta:String="foo".to_string();

letx:u8=1;

//letb:&Foo=&a;

letb=TraitObject{

//almacenalosdatos

data:&a,

//almacenalosmetodos

vtable:&Foo_vtable_para_String

};

//lety:&Foo=x;

lety=TraitObject{

//almacenalosdatos

data:&x,

//almacenalosmetodos

vtable:&Foo_vtable_para_u8

};

//b.metodo();

(b.vtable.metodo)(b.data);

//y.metodo();

(y.vtable.metodo)(y.data);

SeguridaddeObjetosNotodotraitpuedeserusadoparacrearunobjetotrait.Porejemplo,losvectoresimplementanClone,perosiintentamoscrearunobjetotrait:

letv=vec![1,2,3];

leto=&vas&Clone;

Obtenemosunerror:

ElLenguajedeProgramacionRust

245ObjetosTrait

error:cannotconverttoatraitobjectbecausetrait`core::clone::Clone`isnotobject-safe[E0038]

leto=&vas&Clone;

^~

note:thetraitcannotrequirethat`Self:Sized`

leto=&vas&Clone;

^~

ElerrordicequeClonenoes‘seguroparaobjetos’(‘object-safe’).Sololostraitsquesonsegurosparaobjetospuedenserusadosenlacreacióndeobjetostrait.Untraitesseguroparaobjetossiambascondicionessonverdaderas:

eltraitnorequierequeSelf:Sizedtodossusmétodossonsegurosparaobjetos

Entonces,quehaceaunmetodoseguroparaobjetos?CadametododeberequerirqueSelf:Sizedotodasdelassiguientes:

nodebetenerningunaparámetrodetiponodebeusarSelf

Uff!Comopodemosver,casitodasestasreglashablanacercadeSelf.Unabuenaintuiciónseria“exceptuandocircunstanciasespeciales,situmetododetraitusaSelf,noesseguroparaobjetos.”

ElLenguajedeProgramacionRust

246ObjetosTrait

%Closures

Algunasvecesesutilenvolverunafunciónysusvariableslibresparamejorclaridadyreusabilidad.Lasvariableslibresquepuedenserusadasprovienendelámbitoexterioryson‘cerradas’(‘closedover’)cuandosonusadasenlafunción.Deallíelnombre‘closure’.Rustproveeunamuybuenaimplementación,comoveremosacontinuación.

SintaxisLosclosureslucenasí:

letsuma_uno=|x:i32|x+1;

assert_eq!(2,suma_uno(1));

Creamosunenlaceavariable,suma_unoyloasignamosaunclosure.Losargumentosdelclosurevanentrepipes(|),yelcuerpoesunaexpresión,enestecaso,x+1.Recuerdaque{}esunaexpresión,demaneraquepodemostenertambiénclosuresmulti-linea:

letsuma_dos=|x|{

letmutresultado:i32=x;

resultado+=1;

resultado+=1;

resultado

};

assert_eq!(4,suma_dos(2));

Notarasunpardecosasacercadelosclosuresquesonunpocodiferentesdelasfuncionesregularesdefinidasconfn.Loprimeroesquenonecesitamosanotarlostiposdelosargumentoslosvaloresderetorno.Podemos:

letsuma_uno=|x:i32|->i32{x+1};

assert_eq!(2,suma_uno(1));

Perononecesitamoshacerlo.Porque?Básicamente,seimplementodeesamaneraporrazonesdeergonomia.Sibienespecificareltipocompletoparafuncionesconnombreesdeutilidadparacosascomodocumentacióneinferenciadetipos,lafirmacompletaenlos

ElLenguajedeProgramacionRust

247Closures

closuresesraramentedocumentadapuestoaquecasisiempresonanónimos,ynocausanlosproblemasdetipodeerror-a-distanciaquelainferenciaenfuncionesconnombrepuedencausar.

Lasegundasintaxisessimilar,perountantodiferente.Heagregadoespaciosacáparafacilitarlacomparación:

fnsuma_uno_v1(x:i32)->i32{x+1}

letsuma_uno_v2=|x:i32|->i32{x+1};

letsuma_uno_v3=|x:i32|x+1;

Pequenasdiferencias,perosonsimilares.

ClosuresysuentornoElentornoparaunclosurepuedeincluirenlacesavariabledelámbitoquelosenvuelveenadiciónalosparámetrosyvariableslocales.Deestamanera:

letnum=5;

letsuma_num=|x:i32|x+num;

assert_eq!(10,suma_num(5));

Elclosuresuma_num,hacereferenciaaelenlaceletensuámbito:num.Masespecíficamente,tomaprestadoelenlace.Sihacemosalgoqueresulteenunconflictocondichoenlace,obtendríamosunerrorcomoeste:

letmutnum=5;

letsuma_num=|x:i32|x+num;

lety=&mutnum;

Quefallacon:

ElLenguajedeProgramacionRust

248Closures

error:cannotborrow`num`asmutablebecauseitisalsoborrowedasimmutable

lety=&mutnum;

^~~

note:previousborrowof`num`occurshereduetouseinclosure;theimmutable

borrowpreventssubsequentmovesormutableborrowsof`num`untiltheborrow

ends

letsuma_num=|x|x+num;

^~~~~~~~~~~

note:previousborrowendshere

fnmain(){

letmutnum=5;

letsuma_num=|x|x+num;

lety=&mutnum;

}

^

Unerroruntantoverboseperoigualdeutil!Comolodice,nopodemostomarunpréstamomutableennumdebidoaqueelclosureyaestatomándoloprestado.Sidejamoselclosurefueradeámbito,entoncesesposible:

letmutnum=5;

{

letsuma_num=|x:i32|x+num;

}//suma_numsaledeambito,elprestamoterminaaqui

lety=&mutnum;

Sinembargo,situclosureasílorequiere,Rusttomaraperteneciaymoveráelentorno.Losiguientenofunciona:

letnums=vec![1,2,3];

lettoma_nums=||nums;

println!("{:?}",nums);

Obtenemosesteerror:

note:`nums`movedintoclosureenvironmentherebecauseithastype

`[closure(())->collections::vec::Vec]`,whichisnon-copyable

lettoma_nums=||nums;

^~~~~~~

ElLenguajedeProgramacionRust

249Closures

Vec<T>poseeperteneciadesucontenido,yesporello,quealhacerreferenciaaeldentrodenuestroclosure,tenemosquetomarpertenenciadenums.Eslomismoquesihubiéramosproporcionadonumscomoargumentoaunafunciónquetomarapertenenciasobreel.

ClosuresmovePodemosforzarnuestroclosureatomarperteneciadesuentornoconlapalabrareservadamove:

letnum=5;

lettoma_pertenecia_num=move|x:i32|x+num;

Ahora,auncuandolapalabrareservadamoveestapresente,lasvariablessiguenlasemánticanormal.Enestecaso,5implementaCopy,yenconsecuencia,toma_pertenecia_numtomaperteneciadeunacopiadenum.Entonces,cualesladiferencia?

letmutnum=5;

{

letmutsuma_num=|x:i32|num+=x;

suma_num(5);

}

assert_eq!(10,num);

Enestecaso,nuestroclosuretomounareferenciamutableanum,ycuandollamamosasuma_num,estemutoelvalorsubyacente,talycomoloesperábamos.Tambiénnecesitamosdeclararsuma_numcomomut,puestoaqueestamosmutandosuentorno.

Silocambiamosaunclosuremove,esdiferente:

letmutnum=5;

{

letmutsuma_num=move|x:i32|num+=x;

suma_num(5);

}

assert_eq!(5,num);

ElLenguajedeProgramacionRust

250Closures

Obtenemossolo5.Enlugardetomarunprestamomutableennuestronum,tomamosperteneciasobreunacopia.

Otraformadepensaracercadelosclosuresmovees:estosproporcionanaelclosuresupropioregistrodeactivación.Sinmove,elclosurepuedeserasociadoaelregistrodeactivaciónquelocreo,mientrasqueunclosuremoveesautocontenido.Loquesignifica,porejemplo,quegeneralmentenopuedesretornarunclosureno-movedesdeunafunción.

Peroantesdehablarderecibirclosurescomoparámetrosyusarloscomovaloresderetorno,debemoshablarunpocomasacercadesuimplementación.ComounlenguajedeprogramacióndesistemasRustteproporcionaunatoneladadecontrolacercadeloquetucódigohace,ylosclosuresnosondiferentes.

ImplementacióndelosClosuresLaimplementacióndeclosuresdeRustesunpocodiferentealadeotroslenguajes.EnRust,losclosuressonefectivamenteunasintaxisalternaparalostraits.Antesdecontinuar,necesitarashaberleídoelcapitulodetraitsasicomoelcapituloacercadeobjetostrait.

Yaloshasleído?Excelente.

Laclaveparaentendercomofuncionanlosclosuresesalgounpocoextraño:Usar()parallamarunafunción,comofoo(),esunoperadorsobrecargable.Partiendodesdeestapremisa,todolodemásencaja.EnRusthacemosusodeelsistemadetraitsparasobrecargaroperadores.Llamarfuncionesnoesdiferente.Existentrestraitsquepodemossobrecargar:

#modfoo{

pubtraitFn<Args>:FnMut<Args>{

extern"rust-call"fncall(&self,args:Args)->Self::Output;

}

pubtraitFnMut<Args>:FnOnce<Args>{

extern"rust-call"fncall_mut(&mutself,args:Args)->Self::Output;

}

pubtraitFnOnce<Args>{

typeOutput;

extern"rust-call"fncall_once(self,args:Args)->Self::Output;

}

#}

ElLenguajedeProgramacionRust

251Closures

Notarasunaspocasdiferenciasentredichostraits,perounagrandeesself:Fnrecibe&self,FnMuttoma&mutselfyFnOncerecibeself.Loanteriorcubrelostrestiposdeselfatravésdelasintaxisusualdellamadasamétodos.Perohansidoseparadosentrestraits,enlugardeunosolo.Estonosproporcionagrancontrolacercadeltipodeclosuresquepodemosrecibir.

Lasintaxis||{}esunasintaxisalternaparaesostrestraits.Rustgeneraraunstructparaelentorno,impleltraitapropiado,yluegoharáusodeeste.

RecibiendoclosurescomoargumentosAhoraquesabemosquelosclosuressontraits,entoncessabemoscomoaceptaryretornarclosures:justocomocualquierotrotrait!

Loanteriortambiénsignificaquepodemoselegirentredespachoestáticoodinámico.Primero,creemosunafunciónquerecibaalgollamable,ejecuteunallamadasobreelyluegoretorneelresultado:

fnllamar_con_uno<F>(algun_closure:F)->i32

whereF:Fn(i32)->i32{

algun_closure(1)

}

letrespuesta=llamar_con_uno(|x|x+2);

assert_eq!(3,respuesta);

Pasamosnuestroclosure,|x|x+2,allamar_con_uno.llamar_con_unohaceloquesugiere:llamaelclosure,proporcionándole1comoargumento.

Examinemoslafirmadellamar_con_unoconmayordetalle:

fnllamar_con_uno<F>(algun_closure:F)->i32

#whereF:Fn(i32)->i32{

#algun_closure(1)}

Recibimosunparámetro,detipoF.Tambiénretornamosuni32.Estapartenoesinteresante.Lasiguienteloes:

#fnllamar_con_uno<F>(algun_closure:F)->i32

whereF:Fn(i32)->i32{

#algun_closure(1)}

ElLenguajedeProgramacionRust

252Closures

DebidoaqueFnesuntrait,podemoslimitarnuestrogenéricoconel.Enestecaso,nuestroclosurerecibeuni32yretornauni32,esporelloqueellimitedegenéricosqueusamosesFn(i32)->i32.

Hayotropuntoclaveacá:debidoaqueestamoslimitandoungenéricoconuntrait,lallamadaseramonomorfizada,yenconsecuencia,estaremoshaciendodespachoestáticoenelclosure.Esoessupercool.Enmuchoslenguajes,losclosuressoninherentementeasignadosdesdeelmontículo,ycasisiempreinvolucrarandespachodinámico.EnRustpodemosasignarelentornodenuestrosclosuresdesdelapila,asícomodespacharlallamadademaneraestática.Estoocurreconbastantefrecuenciaconlositeradoresysusadaptadores,quienesrecibenclosurescomoargumentos.

Porsupuesto,sideseamosdespachodinámico,podemostenerlotambién.Unobjetotrait,comoesusual,manejaestecaso:

fnllamar_con_uno(algun_closure:&Fn(i32)->i32)->i32{

algun_closure(1)

}

letanswer=llamar_con_uno(&|x|x+2);

assert_eq!(3,answer);

Ahorarecibimosunobjetotrait,un&Fn.Ytenemosquehacerunareferenciaanuestroclosurecuandolopasemosallamar_con_uno,esporelloqueusamos&||.

ApuntadoresafunciónyclosuresUnapuntadorafunciónesunaespeciedeclosurequenoposeeentorno.Comoconsecuencia,podemospasarunapuntadorafunciónacualquierfunciónquerecibaunclosurecomoargumento:

ElLenguajedeProgramacionRust

253Closures

fnllamar_con_uno(algun_closure:&Fn(i32)->i32)->i32{

algun_closure(1)

}

fnsuma_uno(i:i32)->i32{

i+1

}

letf=suma_uno;

letrespuesta=llamar_con_uno(&f);

assert_eq!(2,respuesta);

Enelejemploanteriornonecesitamoslavariableintermediafdemaneraestricta,elnombredelafuncióntambiénsirve:

letrespuesta=llamar_con_uno(&suma_uno);

RetornandoclosuresEsmuycomúnparacódigoconestilofuncionalelretornarclosuresendiversassituaciones.Siintentasretornarunclosure,podríasincurrirenunerror.Alprincipiopuedeparecerextraño,peromasadelanteloentenderemos.Probablementeintentaríasretornarunclosuredesdeunafuncióndeestamanera:

fnfactory()->(Fn(i32)->i32){

letnum=5;

|x|x+num

}

letf=factory();

letrespuesta=f(1);

assert_eq!(6,respuesta);

Loanteriorgeneraestoslargos,perorelacionadoserrores:

ElLenguajedeProgramacionRust

254Closures

error:thetrait`core::marker::Sized`isnotimplementedforthetype

`core::ops::Fn(i32)->i32`[E0277]

fnfactory()->(Fn(i32)->i32){

^~~~~~~~~~~~~~~~

note:`core::ops::Fn(i32)->i32`doesnothaveaconstantsizeknownatcompile-time

fnfactory()->(Fn(i32)->i32){

^~~~~~~~~~~~~~~~

error:thetrait`core::marker::Sized`isnotimplementedforthetype`core::ops::Fn(i32)->i32`[E0277]

letf=factory();

^

note:`core::ops::Fn(i32)->i32`doesnothaveaconstantsizeknownatcompile-time

letf=factory();

^

Pararetornaralgodesdeunafunción,Rustnecesitasabereltamañodeltipoderetorno.PerodebidoqqueFnesuntrait,puedeservariascosasdediversostamaños:muchostipospuedenimplementarFn.Unamanerafácildedarletamañoaalgoestomandounareferenciaaeste,debidoaquelasreferenciastienenuntamañoconocido.Enlugardeloanteriorpodemosescribir:

fnfactory()->&(Fn(i32)->i32){

letnum=5;

|x|x+num

}

letf=factory();

letrespuesta=f(1);

assert_eq!(6,respuesta);

Peroobtenemosotroerror:

error:missinglifetimespecifier[E0106]

fnfactory()->&(Fn(i32)->i32){

^~~~~~~~~~~~~~~~~

Bien.Debidoaquetenemosunareferencia,necesitamosproporcionaruntiempodevida.peronuestrafunciónfactory()norecibeningunargumentoyporellolaelisiondetiemposdevidanoesposibleenestecaso.Entoncesqueopcionestenemos?Intentemoscon'static:

ElLenguajedeProgramacionRust

255Closures

fnfactory()->&'static(Fn(i32)->i32){

letnum=5;

|x|x+num

}

letf=factory();

letrespuesta=f(1);

assert_eq!(6,respuesta);

Peroobtenemosotroerror:

error:mismatchedtypes:

expected`&'staticcore::ops::Fn(i32)->i32`,

found`[closure@:7:9:7:20]`

(expected&-ptr,

foundclosure)[E0308]

|x|x+num

^~~~~~~~~~~

Elerrornosdicequenotenemosun&'staticFn(i32)->i32,sinoun[closure@<anon>:7:9:7:20].Unmomento,que?

DebidoaquenuestroclosuregenerasupropiostructparaelentornoasícomounaimplementaciónparaFn,FnMutyFnOnce,dichostipossonanónimos.Soloexistenparaesteclosure.EsporelloqueRustlosmuestracomoclosure@<anon>envezdealgúnnombreautogenerado.

Elerrortambiénhabladequeseesperaqueeltipoderetornoseaunareferencia,peroloqueestamostratandoderetornarnoloes.Masaun,nopodemosasignardirectamenteuntiempodevida'static'aunobjeto.Entoncestomaremosunenfoquediferenteyretornaremosun‘traitobject’envolviendoelFnenunBox.Losiguientecasifunciona:

fnfactory()->Boxi32>{

letnum=5;

Box::new(|x|x+num)

}

#fnmain(){

letf=factory();

letrespuesta=f(1);

assert_eq!(6,respuesta);

#}

ElLenguajedeProgramacionRust

256Closures

Hayunultimoproblema:

error:closuremayoutlivethecurrentfunction,butitborrows`num`,

whichisownedbythecurrentfunction[E0373]

Box::new(|x|x+num)

^~~~~~~~~~~

Bueno,comodiscutimosanteriormente,losclosurestomansuentornoprestado.Yenestecaso,nuestroentornoestabasadoenun5asignadodesdelapila,lavariablenum.Debidoaestoelprestamoposeeeltiempodevidadelregistrodeactivación.Deretornaresteclosure,lallamadaafunciónpodríaterminar,elregistrodeactivacióndesapareceríaynuestroclosureestaríacapturandounentornodememoriabasura!Conunultimoarreglo,podemoshacerquefuncione:

fnfactory()->Box<Fn(i32)->i32>{

letnum=5;

Box::new(move|x|x+num)

}

#fnmain(){

letf=factory();

letrespuesta=f(1);

assert_eq!(6,respuesta);

#}

AlhacerelclosureinternounmoveFn,hemoscreadounnuevoregistrodeactivaciónparanuestroclosure.EnvolviéndoloconunBox,lehemosproporcionadountamañoconocido,permitiéndoleescaparnuestroregistrodeactivación.

ElLenguajedeProgramacionRust

257Closures

%SintaxisUniversaldeLlamadasaFunción

Algunasveces,variasfuncionespuedentenerelmismonombre.Consideraelsiguientecódigo:

traitFoo{

fnf(&self);

}

traitBar{

fnf(&self);

}

structBaz;

implFooforBaz{

fnf(&self){println!("impldeFooenBaz");}

}

implBarforBaz{

fnf(&self){println!("impldeBarenBaz");}

}

letb=Baz;

Siintentaramosllamarb.f(),obtendriamosunerror:

error:multipleapplicablemethodsinscope[E0034]

b.f();

^~~

note:candidate#1isdefinedinanimplofthetrait`main::Foo`forthetype

`main::Baz`

fnf(&self){println!("Baz’simplofFoo");}

^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

note:candidate#2isdefinedinanimplofthetrait`main::Bar`forthetype

`main::Baz`

fnf(&self){println!("Baz’simplofBar");}

^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Necesitamosunaformadeeliminarlaambigüedad.Estacaracterísticaesdenominada‘sintaxisuniversaldellamadasafunción’,yluceasí:

ElLenguajedeProgramacionRust

258SintaxisUniversaldeLlamadaaFunciones

#traitFoo{

#fnf(&self);

#}

#traitBar{

#fnf(&self);

#}

#structBaz;

#implFooforBaz{

#fnf(&self){println!("impldeFooenBaz");}

#}

#implBarforBaz{

#fnf(&self){println!("impldeBarenBaz");}

#}

#letb=Baz;

Foo::f(&b);

Bar::f(&b);

Analicémosloporpartes.

Foo::

Bar::

Dichasmitadesdeinvocacionsonlostiposdelostraits:FooyBar.Loanterioresresponsabledelaeliminacióndelaambigüedadentrelasdosllamadas:Rustllamalafuncióndeltraitquenombraste.

f(&b)

Cuandollamamosunmétododelaformab.f()usandolasintaxisdemétodos,Rustautomáticamentetomaraprestadoabsif()recibea&self.Enestecaso,Rustnopuedehacerlo.Esporelloquedebemospasara&bdemaneraexplicita.

Usando<>LaformadeSULFdelaqueacabamosdehablar:

Trait::metodo(args);

Esunaversioncorta.Existeunaversionexpandidaquepuedesernecesariaenalgunassituaciones:

::metodo(args);

ElLenguajedeProgramacionRust

259SintaxisUniversaldeLlamadaaFunciones

Lasintaxis<>::esunaformadeproporcionarunapistadetipos.Eltipovadentrodelos<>s.EnestecasoesTypeasTraitindicandoquedeseamosllamaralaversionTraitdemetodo.LasecciónasTraitesopcionaldenoserambigua.Lomismoconlos<>s,deallíprovieneversionmascorta.

Acáunejemplodelusodelaformamaslarga.

traitFoo{

fnclone(&self);

}

#[derive(Clone)]

structBar;

implFooforBar{

fnclone(&self){

println!("CreandounclondeBar");

<BarasClone>::clone(self);

}

}

LoanteriorllamaraelmetodocloneeneltraitClone,envezdelaversionclonedeltraitFoo.

ElLenguajedeProgramacionRust

260SintaxisUniversaldeLlamadaaFunciones

%CratesyModulos

Cuandounproyectocomienzaacrecer,seconsiderabuenapracticadeingenieríadesoftwaredividirloenungrupodecomponentesmaspequeñosqueposteriormenteseránunidos.Tambiénesimportantetenerunainterfazbiendefinida,demaneraqueunapartedelafuncionalidadseaprivada,yotrapublica.Parafacilitaresto,Rustposeeunsistemademódulos.

Terminologiabasica:CratesyModulosRustposeedostérminosdistintosrelacionadosconelsistemademódulos:‘crate’y‘modulo’.Uncrateesunsinónimopara‘biblioteca’or‘paquete’enotroslenguajes.Deallíelnombre“Cargo”delmanejadordepaquetesdeRust:hacesdisponiblestuscratesalosdemásconCargo.Loscratespuedenproducirunejecutablesounabiblioteca,dependiendodelproyecto.

Cadacrateposeeunmoduloraizimplícitoquecontieneelcódigoparadichocrate.Puedesdefinirunárboldesub-modulosbajoelmóduloraíz.Losmódulostepermitenparticionartucodigodentrodelcrate.

Comoejemplo,creemosuncratefrases,quenosproveerávariasfrasesendiferentesidiomas.Paramantenerlascosassimples,nosapegaremossoloa‘saludos’y‘despedidas’comolosdostiposdefrases,asícomoInglesyJaponés(日本語)paralosdosidiomasenlosquelasfrasesestaránrepresentadas.Tendremoslasiguientedistribucióndemódulos:

+-----------+

+---|saludos|

|+-----------+

+----------+|

+---|ingles|---+

|+----------+|+-----------+

|+---|despedidas|

+----------+|+-----------+

|frases|---+

+----------+|+-----------+

|+---|saludos|

|+-----------+|+-----------+

+---|japonés|--+

+-----------+|

|+------------+

+---|despedidas|

+------------+

ElLenguajedeProgramacionRust

261CratesyMódulos

Enesteejemplo,fraseseselnombredenuestrocrate.Elrestosonmódulos.Puedesobservarquelosmódulosformanunárbol,partiendodesdelaraíz,queasuvezeslaraízdelárbolfrasesensi.

Ahoraquetenemosunplan,definamosestosmódulosencódigo.Paracomenzar,generemosunproyectoconCargo:

$cargonewfrases

$cdfrases

Sirecuerdas,loanteriorgeneraunproyectosimple:

$tree.

.

├──Cargo.toml

└──src

└──lib.rs

1directory,2files

src/lib.rseslaraízdenuestrocrate,correspondiendoconfrasesennuestrodiagramaanterior.above.

DefiniendoMódulosParadefinircadaunodenuestrosmódulos,usamoslapalabrareservadamod.Hagamosquenuestrosrc/lib.rsseveaasí:

modingles{

modsaludos{

}

moddespedidas{

}

}

modjapones{

modsaludos{

}

moddespedidas{

}

}

ElLenguajedeProgramacionRust

262CratesyMódulos

Despuésdelapalabrareservadamoddebesproporcionarelnombredelmodulo.LosnombresdedemodulosiguenlasmismasconvencionesquelosdemásidentificadoresenRust:snake_case_en_minusculas.Elcontenidodecadamoduloestadelimitadoporllaves({}).

Dentrodeundeterminadomod,puedesdeclararsub-mods.Podemosreferirnosalossub-modulosconunanotacióndospuntosdobles(::):nuestroscuatromódulosanidadossoningles::saludos,ingles::despedidas,japones::saludos,yjapones::despedidas.Debidoaqueestossub-modulosestándentrodelespaciodenombresdelmodulopadre,losnombresnoentranenconflicto:ingles::saludosyjapones::saludossondistintosinclusocuandosusnombressonambossaludos.

Debidoaqueestecratenoposeeunafunciónmain(),ysellamalib.rs,Cargoconstruiráestecratecomounabiblioteca:

$cargobuild

Compilingfrasesv0.0.1(file:///home/tu/proyectos/frases)

$lstarget/debug

builddepsexampleslibfrases-a7448e02a0468eaa.rlibnative

libfrases-hash.rlibeselcratecompilado.Antesdevercomousarestecratedesdeotro,dividámosloenmultiplesarchivos.

CratesconmultiplearchivosSicadacratefueraunsoloarchivo,dichosarchivosserianbastantegrandes.Esmasfácildividirloscratesenmultiplesarchivos.Rustsoportaestodedosmaneras.

Enlugardedeclararunmoduloasí:

modingles{

//elcontenidodelmodulovaaquí

}

Podemosdeclararlodeestaforma:

modingles;

Sihacemoseso,Rustesperaraencontrarbienunarchivoingles.rsounarchivoingles/mod.rsconelcontenidodenuestromodulo.

ElLenguajedeProgramacionRust

263CratesyMódulos

Notaquetodosenestosarchivos,nonecesitasre-declararelmodulo:conladeclaracióninicialmodessuficiente.

Usandoestasdostécnicas,podemospartirnuestrocrateendosdirectoriosysietearchivos:

$tree.

.

├──Cargo.lock

├──Cargo.toml

├──src

│├──ingles

││├──despedidas.rs

││├──saludos.rs

││└──mod.rs

│├──japones

││├──despedidas.rs

││├──saludos.rs

││└──mod.rs

│└──lib.rs

└──target

└──debug

├──build

├──deps

├──examples

├──libfrases-a7448e02a0468eaa.rlib

└──native

src/lib.rseslaraízdenuestrocrate,yluceasí:

modingles;

modjapones;

EstasdosdeclaracionesledicenaRustquebusquebienseasrc/ingles.rsysrc/japones.rs,osrc/ingles/mod.rsysrc/japones/mod.rs,dependiendodenuestrapreferencia.Enestecaso,debidoaquenuestrosmódulosposeensub-moduloshemosseleccionadolasegunda.Ambossrc/ingles/mod.rsysrc/japones/mod.rssevenlucendeestamanera:

modsaludos;

moddespedidas;

Denuevo,estasdeclaracioneshacenqueRustbusquebienseaporsrc/ingles/saludos.rsysrc/japones/saludos.rsosrc/ingles/despedidas/mod.rsandsrc/japones/despedidas/mod.rs.Debidoaquelossub-modulosnoposeensuspropiossub-

ElLenguajedeProgramacionRust

264CratesyMódulos

modulos,hemosoptadoporusarelenfoquesrc/ingles/saludos.rsandsrc/japones/despedidas.rs.Uff!

Ambossrc/ingles/saludos.rsysrc/japones/despedidas.rsestánvacíosporelmomento.Agreguemosalgunasfunciones.

Colocaestoensrc/ingles/saludos.rs:

fnhola()->String{

"Hello!".to_string()

}

Putthisinsrc/ingles/despedidas.rs:

fnadios()->String{

"Goodbye.".to_string()

}

Putthisinsrc/japones/saludos.rs:

fnhello()->String{

"こんにちは".to_string()

}

Porsupuesto,puedescopiarypegarelJaponesdesdeestapaginaweb,osimplementeescribealgunaotracosa.Noesimportantequeenefectocoloques‘konnichiwa’paraaprenderacercadelsistemademódulosdeRust.

Colocalosiguienteensrc/japones/despedidas.rs:

fnadios()->String{

"さようなら".to_string()

}

(‘Sayōnara’,porsierescurioso.)

Ahoraquetenemosalgodefuncionalidadennuestrocrate,intentemosusarlosdesdeotro.

ImportingExternalCratesYatenemosuncratebiblioteca.Creemosuncrateejecutablequeimporteyusenuestrabiblioteca.

ElLenguajedeProgramacionRust

265CratesyMódulos

Creaunarchivosrc/main.rsycolocaestoenel(nocompilaratodavía):

externcratefrases;

fnmain(){

println!("HolaenIngles:{}",frases::ingles::saludos::hola());

println!("AdiosenIngles:{}",frases::ingles::despedidas::adios());

println!("HolaenJapones:{}",frases::japones::saludos::hola());

println!("AdiosenJapones:{}",frases::japones::despedidas::adios());

}

LadeclaraciónexterncrateleinformaaRustquenecesitamoscompilaryenlazaralcratefrases.Podemosentoncesusarelmodulofrasesaqui.Comomencionamosanteriormente,puedeshacerusodedospuntosdoblesparahacerreferenciaalossub-modulosylasfuncionesdentrodeellos.

Nota:cuandoseimportauncratequetieneguionesensunombre"como-este",locualnoesunidentificadorvalidoenRust,seraconvertidocambiándolelosguionesaguionesbajos,asíqueescribiríasalgocomoexterncratecomo_este;

También,Cargoassumequesrc/main.rseslaraízdeuncratebinario,enlugardeuncratebiblioteca.Nuestropaqueteahoratienedoscrate:src/lib.rsysrc/main.rs.Estepatronesmuycomúnencratesejecutables:lamayoríadelafuncionalidadestaenuncratebiblioteca,yelcrateejecutableusadichabiblioteca.Deestaforma,otrosprogramaspuedentambiénhacerusodelabiblioteca,aunadoaqueofreceunbuenaseparaciónderesponsabilidades.

Loanteriortodavíanofunciona.Obtenemoscuatroerroresquelucensimilaresaestos:

$cargobuild

Compilingfrasesv0.0.1(file:///home/tu/proyectos/frases)

src/main.rs:4:38:4:72error:function`hola`isprivate

src/main.rs:4println!("HolaenIngles:{}",frases::ingles::saludos::hola());

^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

note:inexpansionofformat_args!

<stdmacros>:2:25:2:58note:expansionsite

<stdmacros>:1:1:2:62note:inexpansionofprint!

<stdmacros>:3:1:3:54note:expansionsite

<stdmacros>:1:1:3:58note:inexpansionofprintln!

frases/src/main.rs:4:5:4:76note:expansionsite

PordefectotodoesprivadoenRust.Hablemosdeestoconmayordetalle.

ExportandounaInterfazPublica

ElLenguajedeProgramacionRust

266CratesyMódulos

Rusttepermitecontrolardemaneraprecisacualesaspectosdetuinterfazsonpúblicos,yprivateeseldefacto.Parahaceraalgopublico,debeshacerusodelapalabrareservadapub.Enfoquemonosprimeroenelmoduloingles,paraello,reduzcamosnuestrosrc/main.rsa:

externcratefrases;

fnmain(){

println!("HolaenIngles:{}",frases::ingles::saludos::hola());

println!("AdiosenIngles:{}",frases::ingles::despedidas::adios());

}

Ennuestrosrc/lib.rs,agreguemospubaladeclaracióndelmoduloingles:

pubmodingles;

modjapones;

Yennuestrosrc/ingles/mod.rs,hagamosaambospub:

pubmodsaludos;

pubmoddespedidas;

Ennuestrosrc/ingles/saludos.rs,agreguemospubanuestradeclaraciónfn:

pubfnhola()->String{

"Hola!".to_string()

}

Ytambiénensrc/ingles/despedidas.rs:

pubfnadios()->String{

"Adios.".to_string()

}

Ahora,nuestrocratecompila,conunaspocasadvertenciasacercadenohaberusadolasfuncionesenjapones:

ElLenguajedeProgramacionRust

267CratesyMódulos

$cargorun

Compilingfrasesv0.0.1(file:///home/tu/proyectos/frases)

src/japones/saludos.rs:1:1:3:2warning:functionisneverused:`hola`,#[warn(dead_code)]onbydefault

src/japones/saludos.rs:1fnhola()->String{

src/japones/saludos.rs:2"こんにちは".to_string()

src/japones/saludos.rs:3}

src/japones/despedidas.rs:1:1:3:2warning:functionisneverused:`adios`,#[warn(dead_code)]onbydefault

src/japones/despedidas.rs:1fnadios()->String{

src/japones/despedidas.rs:2"さようなら".to_string()

src/japones/despedidas.rs:3}

Running`target/debug/frases`

HolaenIngles:Hello!

AdiosenIngles:Goodbye.

pubtambiénaplicaalasstructsysuscamposmiembro.YRustteniendosiempresutendenciahacialaseguridad,elhacerunastructpublicnoharáasusmiembrospublicautomáticamente:debesmarcarlosindividualmentecomopub.

Ahoraquenuestrasfuncionessonpublic,podemoshacerusodeellas.Grandioso!Sinembargo,escribirfrases::ingles::saludos::hola()eslargoyrepetitivo.Rustposeeotrapalabrareservadaparaimportarnombresenelámbitoactual,demaneraquepuedashacerreferenciaaellosconnombresmascortos,Hablemosacercadeuse.

ImportandoModuloscon useRustposeeunapalabrareservada,use,quetepermiteimportarnombresentuámbitolocal.Cambiemosnuestrosrc/main.rsparaqueluzcadelasiguientemanera:

externcratefrases;

usefrases::ingles::saludos;

usefrases::ingles::despedidas;

fnmain(){

println!("HolaenIngles:{}",saludos::hola());

println!("AdiosenIngles:{}",despedidas::adios());

}

Lasdoslineasuseimportancadamoduloenelámbitolocal,demaneratalquepodamosreferirnosalasfuncionesconnombresmuchomascortos.Porconvención,cuandoseimportanfunciones,seconsideraunabuenapracticaimportarelmodulo,enlugardelafuncióndirectamente.Enotraspalabras,puedeshaceresto:

ElLenguajedeProgramacionRust

268CratesyMódulos

externcratefrases;

usefrases::ingles::saludos::hola;

usefrases::ingles::despedidas::adios;

fnmain(){

println!("HolaenIngles:{}",hola());

println!("AdiosenIngles:{}",adios());

}

Peronoesidiomático.Hacerlodeestaformatienealtasprobabilidadesdeintroducirunconflictodenombres.Ennuestropequeñoprograma,noesgrancosa,peroamedidaquecrece,sevaconvirtiendoenunproblema.Sitenemosnombresconflictivos,Rustgeneraraunerrordecompilación.Porejemplo,dehaberhechopublicaslasfuncionesdejaponesyhaberintentado:

externcratefrases;

usefrases::ingles::saludos::hola;

usefrases::japones::saludos::hola;

fnmain(){

println!("HolaenIngles:{}",hola());

println!("HolaenJapones:{}",hola());

}

Rustproporcionaríaunerrorentiempodecompilación:

Compilingfrasesv0.0.1(file:///home/tu/frases)

src/main.rs:4:5:4:40error:avaluenamed`hola`hasalreadybeenimportedinthismodule[E0252]

src/main.rs:4usefrases::japones::saludos::hola;

^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

error:abortingduetopreviouserror

Couldnotcompile`frases`.

Siestuviéramosimportandomultiplesnombresdelmismomodulo,nonecesitamosescribirlodosveces.Enlugarde:

usephrases::ingles::saludos;

usephrases::ingles::despedidas;

Podemosusarestaversionmascorta:

ElLenguajedeProgramacionRust

269CratesyMódulos

usephrases::ingles::{saludos,despedidas};

Re-exportandoconpubuseNosolousamosuseparaacortaridentificadores.Puedestambiénusarlosdentrodetucrateparare-exportarunafuncióndentrodeotromodulo.Estotepermitepresentarunainterfazexternaquenecesariamentenomapeededirectamentealaorganizacióninternadetucódigo.

Veamosunejemplo.modificatusrc/main.rsparaqueseleaasí:

externcratefrases;

usefrases::ingles::{saludos,despedidas};

usefrases::japones;

fnmain(){

println!("HolaenIngles:{}",saludos::hola());

println!("AdiosenIngles:{}",despedidas::adios());

println!("HolaenJapones:{}",japones::hola());

println!("AdiosenJapones:{}",japones::adios());

}

Entonces,modificatusrc/lib.rsparahacerelmodulojaponespublico:

pubmodingles;

pubmodjapones;

Acontinuación,hazlasdosfuncionespublicas,primeroensrc/japones/saludos.rs:

pubfnhola()->String{

"こんにちは".to_string()

}

Yluegoensrc/japones/despedidas.rs:

pubfnadios()->String{

"さようなら".to_string()

}

Finalmente,modificatusrc/japones/mod.rsdeestaforma:

ElLenguajedeProgramacionRust

270CratesyMódulos

pubuseself::saludos::hola;

pubuseself::despedidas::adios;

modsaludos;

moddespedidas;

Ladeclaraciónpubusetraelafunciónaelámbitoenestapartedenuestrajerarquíademodulos.Debidoquehemoshechopubusedentrodenuestromodulojapones,ahoratenemosunafunciónfrases::japones::hola()yunafunciónfrases::japones::adios(),auncuandoelcódigoparaellasviveenfrases::japones::saludos::hola()yfrases::japones::despedidas::adios()`.Nuestraorganizacióninternanodefinenuestrainterfazexterna.

Acátenemosunpubuseparacadafunciónquedeseamostraerenelámbitodejapones.Alternativamentepudimoshaberusadolasintaxisalternativadecomodínparaincluirtododesdesaludosenelámbitoactual:pubuseself::saludos::*

Quehayacercadeelself?Bueno,pordefecto,lasdeclaracionesusesonrutasabsolutas,partiendodesdelaraízdetucrate.self,adiferencia,haceesarutarelativaatulugaractualdentrodelajerarquía.Hayunaultimaformaespecialdeusaruse:puedesusarusesuper::paraalcanzarunnivelsuperiorenlajerarquíadesdetuposiciónactual.Algunaspersonasgustanveraselfcomo.ysupercomo..similarmentelosusadosporlosshellsparamostrarlosdirectoriosactualypadrerespectivamente.

Fueradeuse,lasrutassonrelativas:foo::bar()serefiereaunafuncióndentrodefooenrelaciónaendondeestamos.Siposeeunprefijo::,comoen::foo::bar(),entoncesserefiereaunfoodiferente,unarutaabsolutadesdelaraízdetucrate.

Elultimocódigoqueescribimos,compilarayseejecutarasinproblemas:

$cargorun

Compilingfrasesv0.0.1(file:///home/tu/proyectos/frases)

Running`target/debug/frases`

HolainIngles:Hello!

AdiosinIngles:Goodbye.

HolainJapones:こんにちは

AdiosinJapones:さようなら

ImportescomplejosRustofreceunnumerodeopcionesavanzadasquepuedenhacertussentenciasexterncratemascompactasyconvenientes.Heaquíunejemplo:

ElLenguajedeProgramacionRust

271CratesyMódulos

externcratefrasesasdichos;

usedichos::japones::saludosassaludos_ja;

usedichos::japones::despedidas::*;

usedichos::ingles::{self,saludosassaludos_en,despedidasasdespedidas_en};

fnmain(){

println!("HolaenIngles;{}",saludos_en::hola());

println!("YenJapones:{}",saludos_ja::hola());

println!("GoodbyeinIngles:{}",ingles::despedidas::adios());

println!("Otravez:{}",despedidas_en::adios());

println!("YenJapones:{}",adios());

}

Queestapasando?

Primero,ambosexterncrateyusepermitenrenombrarloqueestasiendoimportado.Entonceselcratetodavíasellama"frases",peroaquínosreferiremosaelcomo"dichos".Similarmente,elprimerusetraeelmodulojapones::saludosdesdeelcrate,perolohacedisponibleatravésdelnombresaludos_jaenlugardesimplementesaludos.Loanteriorpuedeayudaraevitarambigüedadcuandoseimportannombressimilaresdesdedistintoslugares.

Elsegundouseposeeunasteriscoparatraertodoslossímbolosdesdeelmodulodichos::japones::despedidas.ComopodrásvermastardepodemosreferirnosaladiosJaponessincalificadoresdemodulo.Estetipodeglobdebeserusandoconcautela.

Elterceruserequiereunpocomasdeexplicación.Estausando"expansiondellaves"paracomprimirtressentenciasuseenuna(estetipodesintaxispuedesertefamiliarsihasescritoscriptsdelshelldeLinux).Laformadescomprimidadeestasentenciaseria:

usedichos::ingles;

usedichos::ingles::saludosassaludos_en;

usedichos::ingles::despedidasasdespedidas_en;

Comopuedesver,lasllavescomprimenlassentenciasuseparavariositemsbajolamismaruta,yenestecontextoselfhacereferenciaadicharuta.Nota:Lasllavesnopuedenseranidadasomezcladasconglobbingdeasteriscos.

ElLenguajedeProgramacionRust

272CratesyMódulos

%constystatic

Rustposeeunamaneradedefinirconstantesconlapalabrareservadaconst:

constN:i32=5;

Adiferenciadelosenlacesavariablelet,debesanotareltipodeunconst.

Lasconstantesvivenporeltiempodevidacompletodelprograma.Parasermasespecíficos,lasconstantesenRustnotienendireccionesdememoriafijas.Estoesdebidoaqueestassoninsertadasenlineaencadalugarenelquesonusadas.Porestarazón,referenciasalamismaconstantenoestángarantizadasaapuntaralamismadireccióndememoria.

static

Rustproporcionaunafacilidaddetipo‘variableglobal’enitemsestáticos.Sonsimilaresalasconstantes,perolositemsestáticosnosoninsertadosenlineatrassuuso.Estosignificaquesoloexisteunainstanciaparacadavalor,yestaubicadaenunadireccióndememoriafija.

Heaquiunejemplo:

staticN:i32=5;

Adiferenciadelosenlacesavariablelet,debesanotareltipodeunstatic.

Lositemsstaticvivenporeltiempodevidadelprogramacompleto,yenconsecuenciacualquierreferenciaesalmacenadaenunaconstanteposeeuntiempodevida'static:

staticNOMBRE:&'staticstr="Steve";

MutabilidadPuedesintroducirmutabilidadconlapalabrareservadamut:

staticmutN:i32=5;

ElLenguajedeProgramacionRust

273`const`y`static`

Debidoqueesmutable,unhilopodríaestaractualizandoNmientrasqueotroesteleyéndolo,causandoinseguridadenelmanejodememoria.Esporelloqueamboselaccesoylamutacióndeunstaticmutsoninseguros,yenconsecuenciadebenserefectuadosdentrodeunbloqueunsafe:

#staticmutN:i32=5;

unsafe{

N+=1;

println!("N:{}",N);

}

Aunadoaesto,cualquiertipoalmacenadoenunstaticdebeserSync,ypodríanotenerunaimplementacióndeDrop.

InicializaciónAmbosconstystatictienenrequerimientosalproporcionarlesunvalor.Soloselespuedeproporcionarunvalorqueseaunaexpresiónconstante.Enotraspalabras,nopuedesusarelresultadodeunallamadaafunción,algosimilarmentecomplejooalgodeterminadoentiempodeejecución.

Cualconstruccióndeberíausar?Casisiempre,sipuedesescogerentrelasdos,eligeconst.Esbastanteraroquequierastenerunadireccióndememoriaasociadacontuconstante,también,elusarconstpermiteoptimizacionescomopropagacióndeconstantesnosoloentucratesinoenloscratessubyacentes.

ElLenguajedeProgramacionRust

274`const`y`static`

%Atributos

Lasdeclaracionespuedenseranotadascon‘atributos’enRust.Losatributoslucenasí:

#[test]

#fnfoo(){}

odeestamanera:

#modfoo{

#![test]

#}

Ladiferenciaentrelosdosesel!,quecambiaaquecosaaplicaelatributo:

#[foo]

structFoo;

modbar{

#![bar]

}

Elatributo#[foo]aplicaaelsiguienteitem,queesladeclaracióndelstruct.Elatributo#![bar]aplicaaelitemqueloencierra,ladeclaraciónmod.Deresto,sonlomismo.Amboscambiandealgunaformaelsignificadodeelitemalcualestánasociados.

Porejemplo,consideraunafuncióncomoesta:

#[test]

fncomprobar(){

assert_eq!(2,1+1);

}

Estamarcadocon#[test].Estosignificaqueesespecial:cuandoejecuteslaspruebas,estafunciónseraejecutada.Cuandocompilasnormalmente,noseraincluidanisiquiera.Lafunciónesahoraunafuncióndeprueba.

Losatributospuedentenertambiéndataadicional:

#[inline(always)]

fnfn_super_rapida(){

#}

Oinclusoclavesyvalores:

ElLenguajedeProgramacionRust

275Atributos

#[cfg(target_os="macos")]

modsolo_macos{

#}

LosatributossonusadosparaunnumerodecosasdiferentesenRust.Hayunalistadeatributoscompletaenlareferencia.Actualmente,notienespermitidocreartuspropiosatributos,elcompiladordeRustlosdefine.

ElLenguajedeProgramacionRust

276Atributos

%Aliastype

Lapalabrareservadatypetepermitedeclararunaliasaotrotipo:

typeNombre=String;

Ahora,puedesusarestetipocomosifuerauntiporeal:

typeName=String;

letx:Nombre="Hola".to_string();

Sinembargo,nota,queesunalias,nounnuevotipo.Enotraspalabras,debidoaqueRustesfuertementetipificado,podríasesperarqueunacomparaciónentredostiposdiferentesfalle:

letx:i32=5;

lety:i64=5;

ifx==y{

//...

}

loanterior,produce:

error:mismatchedtypes:

expected`i32`,

found`i64`

(expectedi32,

foundi64)[E0308]

ifx==y{

^

Pero,situviéramosunalias:

typeNum=i32;

letx:i32=5;

lety:Num=5;

ifx==y{

//...

}

ElLenguajedeProgramacionRust

277Alias`type`

Compilaríasinerrores.ValoresdeuntipoNumsonlomismoqueunvalordetipoi32,entodoslosaspectos.

Puedestambiénhacerusodealiasdetiposengenéricos:

usestd::result;

enumErrorConcreto{

Foo,

Bar,

}

typeResult<T>=result::Result<T,ErrorConcreto>;

Elcódigoanterior,creaunaversionespecializadadeeltipoResult,lacualposeesiempreunErrorConcretoparalaparteEdeResult<T,E>.Estapracticaesusadacomúnmenteenlabibliotecaestándarparalacreacióndeerrorespersonalizadosparacadasub-seccion.Porejemplo,io::Result.

ElLenguajedeProgramacionRust

278Alias`type`

%ConversiónEntreTipos

Rust,consufocoenseguridad,proporcionadosformasdiferentesdeconversiónentretipos.Laprimera,as,esparaconversiónsegura.Encontraste,transmutepermiteconversionarbitraria,yesunadelascaracterísticasmaspeligrosasdeRust!

as

Lapalabrareservadaasllevaacaboconversionbásica:

letx:i32=5;

lety=xasi64;

as,sinembargo,solopermiteciertostiposdeconversión:

leta=[0u8,0u8,0u8,0u8];

letb=aasu32;//cuatroochoshacen32

Loanteriorproduceunerror:

error:non-scalarcast:`[u8;4]`as`u32`

letb=aasu32;//cuatroochoshacen32

^~~~~~~~

Esuna‘conversionnoescalar’(‘non-scalarcast’)porquetenemosmultiplesvalores:loscuatroelementosenelarreglo.Estetipodeconversionessonmuypeligrosas,debidoaqueasumencosasacercadecomolasmultiplesestructurassubyacentesestánimplementadas.Paraesto,necesitamosalgomaspeligroso.

transmute

Lafuncióntransmuteesproporcionadaporunaintrínsecadelcompilador,yloquehaceesmuysimple,peroalmismotiempomuypeligroso.LediceaRustquetrateunvalordeuntipocomosifueraunvalordeotrotipo.Llevaacaboestosinrespetarelsistemadetipos,yconfíacompletamenteenti.

ElLenguajedeProgramacionRust

279ConversiónentreTipos

Ennuestroejemploanterior,sabemosquenuestroarreglodecuatrou8srepresentaunu32demaneracorrecta,porlotantoqueremoshacerlaconversión.UsandotransmuteenlugardeasRustnospermite:

usestd::mem;

unsafe{

leta=[0u8,0u8,0u8,0u8];

letb=mem::transmute::<[u8;4],u32>(a);

}

Debemosenvolverlaoperaciónenunbloqueunsafeparaqueelcódigoanteriorcompilesatisfactoriamente.Técnicamente,sololallamadaamem::transmutenecesitaestardentrodelbloque,peroenestecasoestabientenertodolorelacionadoalaconversiondemaneraquesepasendondebuscar.Enestecaso,losdetallesacercadeasontambiénimportantes,esporelloqueestándentrodelbloque.Verascódigoencualquieradelosdosestilos,algunasveceselcontextoestatanlejosqueenvolvertodoelcódigoenunbloqueunsafenoesunagranidea.

Mientrasquetransmutehacemuypocoschequeos,seasegura,almenos,quelostiposseandelmismotamaño.Losiguiente,falla:

usestd::mem;

unsafe{

leta=[0u8,0u8,0u8,0u8];

letb=mem::transmute::<[u8;4],=""u64="">(a);

}

con:

error:transmutecalledontypeswithdifferentsizes:[u8;4](32bits)tou64

(64bits)

Dichoesto,estásportucuenta!

ElLenguajedeProgramacionRust

280ConversiónentreTipos

%TiposAsociados

LostiposasociadossonunapartepoderosadelsistemadetiposdeRust.Serelacionanconlaideadeuna‘familiadetipos’,enotraspalabras,laagrupacióndemultiplestipos.Esadescripciónesunapocoabstracta,esmejorquenosadentremosdeunavezenunejemplo.SiqueremosescribiruntraitGrafo,tenemosdostiposporencimadeloscualesdebemossergenéricos:eltipodelosnodosyeltipodelosvertices.Podríamosescribiruntrait,Grafo<N,V>comoeste:

traitGrafo<N,V>{

fntiene_vertice(&self,&N,&N)->bool;

fnvertices(&self,&N)->Vec<V>;

//etc

}

Sibienestodealgunamanerafunciona,terminasiendounpocoraro.Porejemplo,cualquierfunciónquequierarecibirunGrafocomoparámetroahoratambiénnecesitasergenéricaporsobrelostiposNodoyVertice:

fndistancia>(grafo:&G,inicio:&N,fin:&N)->u32{...}

NuestrocalculodeladistanciafuncionasintomarencuentanuestrotipoVértice,enconsecuencialaVenestafirmaessimplementeunadistracción.

LoquerealmentequeremosexpresaresqueciertostiposVerticeyNodovienenjuntosparaformarcadaclasedeGrafo.Podemoshacerestocontiposasociados:

traitGrafo{

typeN;

typeV;

fntiene_vertice(&self,&Self::N,&Self::N)->bool;

fnvertices(&self,&Self::N)->Vec<Self::V>;

//etc

}

Ahora,nuestrosclientespuedenabstraerseporencimadeundeterminadoGrafo:

fndistancia(grafo:&G,inicio:&G::N,fin:&G::N)->u32{...}

SinnecesidaddelidiarconeltipoVertice!

Echemosunvistazoconmayordetalleatodoesto.

ElLenguajedeProgramacionRust

281TiposAsociados

DefiniendotiposasociadosConstruyamosesetraitGrafo.Heaquíladefinición:

traitGraph{

typeN;

typeV;

fntiene_vertice(&self,&Self::N,&Self::N)->bool;

fnvertices(&self,&Self::N)->Vec<Self::V>;

}

Simple.Lostiposasociadosusanlapalabrareservadatype,yvandentrodelcuerpodeltraitenconjuntoconlasfunciones.

Dichasdeclaracionestypepuedentenerlomismoquelasfunciones.PorejemplosideseáramosquenuestrotipoNimplementaseDisplay,demaneraquepudiésemosimprimirlosnodos,podríamoshacerlosiguiente:

usestd::fmt;

traitGrafo{

typeN:fmt::Display;

typeV;

fntiene_vertice(&self,&Self::N,&Self::N)->bool;

fnvertices(&self,&Self::N)->Vec<Self::V>;

}

ImplementandotiposasociadosJustocomocualquierotrotrait,lostraitsqueusantiposasociadoshacenusodelapalabrareservadaimplparaproporcionarimplementaciones.AcontinuaciónunaimplementaciónsimpledeGrafo:

ElLenguajedeProgramacionRust

282TiposAsociados

#traitGrafo{

#typeN;

#typeV;

#fntiene_vertice(&self,&Self::N,&Self::N)->bool;

#fnvertices(&self,&Self::N)->Vec<Self::V>;

#}

structNodo;

structVertice;

structMiGrafo;

implGrafoforMiGrafo{

typeN=Nodo;

typeV=Vertice;

fntiene_vertice(&self,n1:&Nodo,n2:&Nodo)->bool{

true

}

fnvertices(&self,n:&Nodo)->Vec<Vertice>{

Vec::new()

}

}

EstatontaimplementaciónsiempreretornatrueyunVec<Vertice>vacío,perotedaunaideadecomoseimplementaestetipodetraitscontiposasociados.Primeronecesitamostresstructs,unaparaelgrafo,unaparaelnodo,yunaparaelvértice.Dehabertenidomassentidousaruntipodiferente,tambiénhubiesefuncionado,ahorausaremosstructsparalostres.

Losiguienteeslalineaimpl,queesidénticaalaimplementacióndecualquierotrotrait.

Deaquíenadelante,usamos=paradefinirnuestrostiposasociados.Elnombrequeeltraitusavadelladoizquierdodel=,yeltipoenconcretoparaelqueestamosimplementandoestetraitvadelladoderecho.Finalmente,podemosusartiposconcretosennuestrasdeclaracionesdefunción.

ObjetostraitcontiposasociadosHayotrasintaxisdelacualdebemoshablar:objetostrait.Sideseáramoscrearunobjetotraitapartirdeuntipoasociadodeestamanera:

ElLenguajedeProgramacionRust

283TiposAsociados

#traitGrafo{

#typeN;

#typeV;

#fntiene_vertice(&self,&Self::N,&Self::N)->bool;

#fnvertices(&self,&Self::N)->Vec;

#}

#structNodo;

#structVertice;

#structMiGrafo;

#implGrafoforMiGrafo{

#typeN=Node;

#typeV=Vertice;

#fntiene_vertice(&self,n1:&Nodo,n2:&Nodo)->bool{

#true

#}

#fnvertices(&self,n:&Nodo)->Vec{

#Vec::new()

#}

#}

letgrafo=MiGrafo;

letobj=Box::new(grafo)asBox;

Obtendríamosestosdoserrores:

error:thevalueoftheassociatedtype`V`(fromthetrait`main::Grafo`)must

bespecified[E0191]

letobj=Box::new(grafo)asBox;

^~~~~~~~~~~~~~~~~~~~~~~~~~~~~

24:44error:thevalueoftheassociatedtype`N`(fromthetrait

`main::Grafo`)mustbespecified[E0191]

letobj=Box::new(grafo)asBox;

^~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Nopodemoscrearobjetostraitdeestamanera,puestoaquenoconocemoslostiposasociados.Ensulugarpodríamosescribir:

ElLenguajedeProgramacionRust

284TiposAsociados

#traitGrafo{

#typeN;

#typeV;

#fnhas_edge(&self,&Self::N,&Self::N)->bool;

#fnedges(&self,&Self::N)->Vec<Self::V>;

#}

#structNodo;

#structVertice;

#structMiGrafo;

#implGrafoforMiGrafo{

#typeN=Nodo;

#typeV=Vertice;

#fntiene_vertice(&self,n1:&Nodo,n2:&Nodo)->bool{

#true

#}

#fnvertices(&self,n:&Nodo)->Vec<Vertice>{

#Vec::new()

#}

#}

letgrafo=MiGrafo;

letobj=Box::new(grafo)asBox<Grafo<N=Nodo,V=Vertice>>;

LasintaxisN=Nodonospermitecrearuntipoconcreto,Nodo,paraelparámetrodetipoN.LomismoconV=Vertice.Denohaberproporcionadoestarestricción,nohubiéramospodidodeterminarcontracualimpldebeserusadoelobjetotrait.

ElLenguajedeProgramacionRust

285TiposAsociados

%TipossinTamaño

Lamayoríadelostiposposeeuntamañoparticular,enbytes,queesconocidoentiempodecompilación.Porejemplouni32tieneuntamañodetreintaydosbits,ocuatrobytes.Sinembargo,hayalgunostiposquesonútilesdeexpresar,peronotienenuntamañodefinido.Dichostipossondenominadostipos‘sintamaño’(‘unsized’)o‘detamañodinámico’(‘dynamicallysized’).Unejemploes[T].DichotiporepresentaciertonumerodeTensecuencia.Peronosabemoscuantosdeelloshay,yenconsecuenciaeltamañoesdesconocido.

Rustentiendealgunosdeestostipos,perotienenalgunasrestricciones.Sontres:

1. Solopodemosmanipularunainstanciadeuntiposintamañoatravésdeunapuntador.Un&[T]funcionaperfecto,peroun[T]no.

2. Lasvariablesyargumentosnopuedentenertiposdetamañodinámico.3. Soloelultimocampoenunstructpuedeteneruntipodedatosdetamañodinámico;

losotroscamposno.Lasvariantesdeenumsnodebentenertiposdetamañodinámicocomodata.

Entonces,porquemolestarse?Bueno,debidoaque[T]puedesoloserusadodetrásdeunapuntador,denotenersoporteaniveldelenguajeparatipossintamaño,seriaimposibleescribirlosiguiente:

implFooforstr{

o

implFoofor[T]{

Ensulugar,tendríasqueescribir:

implFoofor&str{

Significandoqueestaimplementaciónsolofuncionariaparareferencias,ynootrotiposdeapuntadores.Conelimplforstr,todoslosapuntadores,incluyendo(enalgúnpunto,hayalgunosbugsquearreglarprimero)apuntadoresinteligentesdefinidosporelusuario,puedenusaresteimpl.

?Sized

ElLenguajedeProgramacionRust

286TipossinTamaño

Sideseasescribirunafunciónqueacepteuntipodedatosdetamañodinámico,puedesusarlimitedetraitespecial,?Sized:

structFoo<T:?Sized>{

f:T,

}

Este?,leídocomo“TpuedeserSized”,significaqueestelimitedetraitesespecial:nospermitehacermatchconmastipos,nomenos.EsjustocasicomoquecualquierTimplícitamenteesT:Sized,yel?deshaceestecomportamientopordefecto.

ElLenguajedeProgramacionRust

287TipossinTamaño

%OperadoresySobrecarga

Rustpermiteunaformalimitadadesobrecargadeoperadores.Existenciertosoperadoresquepuedensersobrecargados.Parasoportarunoperadorparticularentretipos,hayuntraitenespecificoquepuedesimplementar,elcualsobrecargaeloperador.

Porejemplo,eloperador+puedesersobrecargadoconeltraitAdd:

usestd::ops::Add;

#[derive(Debug)]

structPunto{

x:i32,

y:i32,

}

implAddforPunto{

typeOutput=Punto;

fnadd(self,otro:Punto)->Punto{

Punto{x:self.x+otro.x,y:self.y+otro.y}

}

}

fnmain(){

letp1=Punto{x:1,y:0};

letp2=Punto{x:2,y:3};

letp3=p1+p2;

println!("{:?}",p3);

}

Enmain,podemoshacerusode+ennuestrosdosPuntoss,puestoquehemosimplementadoAdd<Output=Punto>paraPunto.

Existeunnumerodeoperadoresquepuedensersobrecargadosdeestamanera,ytodossustraitsasociadosvivenenelmodulostd::ops.Echaunvistazoaladocumentaciónparaunalistacompleta.

Implementardichostraitssigueunpatron.AnalicemosaAddconmasdetalle:

#modfoo{

pubtraitAdd<RHS=Self>{

typeOutput;

fnadd(self,rhs:RHS)->Self::Output;

}

#}

ElLenguajedeProgramacionRust

288OperadoresySobrecarga

Hayentotaltrestiposinvolucradosaqui:EltipoparaelcualestasimplementadoAdd,RHS(derighthandside),quepordefectoesSelf,yOutput.Paraunaexpresionletz=x+y,xeseltipoSelf,yeselRHS,yzeseltipoSelf::Output.

#structPunto;

#usestd::ops::Add;

implAdd<i32>forPunto{

typeOutput=f64;

fnadd(self,rhs:i32)->f64{

//sumaruni32aunPuntoobteniendounf64

#1.0

}

}

tepermitirahacerlosiguiente:

letp:Point=//...

letx:f64=p+2i32;

UsandolostraitsdeoperadorenestructurasgenericasAhoraquesabemoscomoestándefinidoslostraitsdeoperador,podemosdefinirnuestrotraitTieneAreaynuestraestructuraCuadradodelcapitulodetraitsdeunaformamasgenérica:

ElLenguajedeProgramacionRust

289OperadoresySobrecarga

usestd::ops::Mul;

traitTieneArea<T>{

fnarea(&self)->T;

}

structCuadrado<T>{

x:T,

y:T,

lado:T,

}

impl<T>TieneArea<T>forCuadrado<T>

whereT:Mul<Output=T>+Copy{

fnarea(&self)->T{

self.lado*self.lado

}

}

fnmain(){

lets=Cuadrado{

x:0.0f64,

y:0.0f64,

lado:12.0f64,

};

println!("Areades:{}",s.area());

}

ParaTieneAreayCuadrado,solodeclaramosunparametrodetipoTyreemplazamoself64.Elbloqueimplnecesitamasmodificaciones:

implTieneAreaforCuadrado

whereT:Mul+Copy{...}

Elmétodoarearequierequepodamosmultiplicarloslados,esporelloquedeclaramosqueTdebeimplementarstd::ops::Mul.ComoAdd,mencionadoanteriormente,MultomaunparámetroOutput:debidoaquesabemosquelosnúmerosnocambiandetipocuandosonmultiplicados,tambiénlodefinimoscomoT.Ttambiéndebesoportarcopiado,demaneraqueRustnotratedemoverself.ladoaelvalorderetorno.

ElLenguajedeProgramacionRust

290OperadoresySobrecarga

%CoercionesDeref

LabibliotecaestándarproporcionauntraitespecialDeref.Esusadonormalmenteparasobrecargar*,eloperadordedereferencia:

usestd::ops::Deref;

structEjemploDeref<T>{

valor:T,

}

impl<T>DerefforEjemploDeref<T>{

typeTarget=T;

fnderef(&self)->&T{

&self.valor

}

}

fnmain(){

letx=EjemploDeref{valor:'a'};

assert_eq!('a',*x);

}

Loanterioresútilparaescribirtipospersonalizadosdeapuntadores.Sinembargo,hayunafacilidaddellenguajerelacionadaaDeref:las‘coercionesderef’.Heaquílaregla:SitienesuntipoU,yesteimplementaDeref<Target=T>,losvaloresde&UharáncoercionAutomaticaaun&T.Acontinuaciónunejemplo:

fnfoo(s:&str){

//tomarlacadenaprestadaporunsegundo

}

//StringimplementaDeref<Target=str>

letowned="Hola".to_string();

//entonces,estofunciona:

foo(&owned);

Usandounampersandenfrentedelvalortomamosunareferenciaael.EntoncesownedesunString,&ownedesun&String,ydebidoaimplDeref<Target=str>paraString,&Stringharáderefa&strqueestomadoporfoo().

Esoestodo.DichareglaesunodelosúnicoslugaresenlosqueRusthaceconversionesautomáticaspornosotros,peroalmismotiempoagregamuchaflexibilidad.PorejemploeltipoRc<T>implementaDeref<Target=T>,demaneraqueestofunciona:

ElLenguajedeProgramacionRust

291CoercionesDeref

usestd::rc::Rc;

fnfoo(s:&str){

//tomarlacadenaprestadaporunsegundo

}

//StringimplementaDeref<Target=str>

letowned="Hello".to_string();

letcontado=Rc::new(owned);

//entonces,estofunciona:

foo(&contado);

TodoloquehemoshechoesenvolvernuestroStringenunRc<T>.PeroahorapodemospasarelRc<String>acualquierlugarenelcualtengamosunaString.Lafirmadefoonocambio,perofuncionadeigualformaconcualquieradelosdostipos.Esteejemplotienedosconversiones:deRc<String>aStringyluegodeStringa&str.Rustharáestotantasvecescomoseaposiblehastaquelostiposcoincidan.

Otraimplementaciónmuycomúnproporcionadaporlabibliotecaestándares:

fnfoo(s:&[i32]){

//tomarelpedazoprestadoporunsegundo

}

//Vec<T>implementaDeref<Target=[T]>

letowned=vec![1,2,3];

foo(&owned);

LosvectorespuedenhacerDerefaunslice.

DerefyllamadasametodoDereftambiénentraraenefectocuandosellameaunmétodo.Consideraelsiguienteejemplo:

ElLenguajedeProgramacionRust

292CoercionesDeref

structFoo;

implFoo{

fnfoo(&self){println!("Foo");}

}

letf=&&Foo;

f.foo();

Aunquefesun&&FooyFoorecibea&self,loanteriorfunciona.Todoestocomoconsecuenciadequetodasestascosassonlomismo:

f.foo();

(&f).foo();

(&&f).foo();

(&&&&&&&&f).foo();

Unvalordetipo&&&&&&&&&&&&&&&&FoopuedeauntenerllamadasamétodosdefinidosenFoodebidoaqueelcompiladorinsertaratantasoperacionesseannecesariasparahacerlofuncionar.Ydebidoaqueinserta`s,sehaceusodeDeref`.

ElLenguajedeProgramacionRust

293CoercionesDeref

%Macros

Porahora,hasaprendidomuchosobrelasherramientasqueRustofreceparaabstraeryreutilizarcódigo.Estasunidadesdereutilizaciónposeenunaricaestructurasemántica.Porejemplo,lasfuncionestienenunafirmadetipos,losparámetrosdetipotienenlímitesdetraitsylasfuncionessobrecargadasdebenpertenecerauntraitparticular.

EstaestructurasignificaquelasprincipalesabstraccionesenRustposeenunpoderosomecanismodechequeoentiempodecompilación.Pero,elprecioesunaflexibilidadreducida.Siseidentificavisualmenteunpatróndecódigorepetido,podríaserdifícilotediosoexpresaresepatróncomounafuncióngenérica,untrait,ocualquierotroelementodelasemánticadeRust.

Lasmacrosnospermitenabstraeraunnivelsintáctico.Unainvocacióndemacroeslaabreviaturadeunaformasintáctica"expandida".Dichaexpansiónocurredurantelacompilación,antesdecomprobaciónestática.Comoresultado,lasmacrospuedencapturarmuchospatronesdereutilizacióndecódigoquelasabstraccionesfundamentalesdeRustnopueden.

Elinconvenienteesqueelcódigobasadoenmacrospuedesermásdifícildeentender,porquemenosdelasnormasinternasdeRustaplican.Aligualqueunafunciónordinaria,unamacrobienhechasepuedeutilizarsinentenderdetallesdeimplementación.Sinembargo,puedeserdifícildiseñarunamacroconunbuencomportamiento!Además,loserroresdecompilaciónencódigodemacrossonmásdifícilesdeentender,porquedescribenproblemasenelcódigoexpandido,noaniveldelcódigofuentequeusanlosdesarrolladores.

Estosinconvenienteshacendelasmacrosuna"herramientadeúltimorecurso".Loanteriornoquieredecirquelasmacrossonmalas;formanpartedeRustporqueavecessonnecesariasparacódigoconcisoyabstracto.Simplementemanténencuentaesteequilibrio.

DefiniendounamacroPuedequeyahayasvistolamacrovec!,utilizadaparainicializarunvectorconunnumerocualquieradeelementos.

letx:Vec<u32>=vec![1,2,3];

#assert_eq!(&[1,2,3],&x);

Estonopuedeserunafunciónordinaria,porqueaceptacualquiernúmerodeargumentos.Peropodemosimaginarlocomounaabreviaciónsintácticapara

ElLenguajedeProgramacionRust

294Macros

letx:Vec<u32>={

letmuttemp_vec=Vec::new();

temp_vec.push(1);

temp_vec.push(2);

temp_vec.push(3);

temp_vec

};

#assert_eq!(&[1,2,3],&x);

Podemosimplementarestaabreviatura,utilizandounamacro:actual

actual.Lapropiadefinicióndevec!enlibcollectionsdifieredela↩

presentadaaquí,porrazonesdeeficienciayreutilización.Algunas

deellassonmencionadasenel[capituloavanzadodemacros][].

macro_rules!vec{

($($x:expr),*)=>{

{

letmuttemp_vec=Vec::new();

$(

temp_vec.push($x);

)*

temp_vec

}

};

}

#fnmain(){

#assert_eq!(vec![1,2,3],[1,2,3]);

#}

¡Whoa!,unmontóndesintaxisnueva.Examinémosloparteporparte.

macro_rules!vec{...}

Loanteriordicequeestamosdefiniendounamacrollamadavec,aligualquefnvecdefiniríaunafunciónllamadavec.Enprosa,informalmenteescribimoselnombredeunamacroconunsignodeexclamación,porejemplo,vec!.Estesignodeexclamaciónespartedelasintaxisdeinvocaciónysirveparadistinguirunamacrodeunafunciónordinaria.

Coincidenciadepatrones

ElLenguajedeProgramacionRust

295Macros

Unamacrosedefineatravésdeunaseriedereglas,lascualessoncasosdecoincidenciadepatrones.Anteriormentevimos

($($x:expr),*)=>{...};

Estoessimilaraunbrazodeunaexpresiónmatch,perolaspruebasparacoincidenciaocurrensobrelosárbolesdesintaxisRustentiempodecompilación.Elpuntoycomaesopcionalenelcasofinal(aquí,elúnicocaso).El"patrón"enelladoizquierdodel=>esconocidocomoun'matcher'.Losmatcherstienensupropiapequeñogramáticadentrodellanguage.

Elmatcher$x:exprcoincidiráconcualquierexpresiónRust,asociandoeseárbolsintácticoala'metavariable'$x.Elidentificadorexpresun'especificadorfragmento';todaslasposibilidadesseenumeranenelcapituloavanzadodemacros.Alrodearelmatchercon$(...),*,coincidiráconceroomásexpresionesseparadasporcomas.

Ademasdelasintaxisespecialdematchers,lostokensRustqueaparecenenunmatcherdebencoincidirdemaneraexacta.Porejemplo:

macro_rules!foo{

(x=>$e:expr)=>(println!("modoX:{}",$e));

(y=>$e:expr)=>(println!("modoY:{}",$e));

}

fnmain(){

foo!(y=>3);

}

imprimirá

modoY:3

Con

foo!(z=>3);

obtenemoselerrordelcompilador

error:norulesexpectedthetoken`z`

Expansión

ElLenguajedeProgramacionRust

296Macros

ElladoderechodeunareglamacroessintaxisRustordinaria,ensumayorparte.Peropodemosinsertarpartesdesintaxiscapturadasporelmatcher.Delejemplooriginal:

$(

temp_vec.push($x);

)*

Cadaexpresióncoincidente$xproduciráunasolaexpresiónpushenlaexpansióndelamacro.Larepeticiónsedesarrollaen"lockstep"conlarepeticiónenelmatcher(mássobreestoenunmomento).

Debidoaque$xyafuemarcadocomounacoincidenciaconunaexpresión,norepetimos:exprenelladoderecho.Además,noincluimosunacomaseparandocomopartedeloperadorderepetición.Encambio,tenemosunpuntoycomaqueterminadentrodelbloquerepetido.

Otrodetalle:lamacrovectienedosparesdellavesenelladoderecho.Amenudosecombinandeestemodo:

macro_rules!foo{

()=>{{

...

}}

}

Lasllavesexterioressonpartedelasintaxisdemacro_rules!.Dehecho,sepuedeutilizar()o[]ensulugar.Simplementedelimitanelladoderechocomountodo.

Lasllavesinterioressonpartedelasintaxisexpandida.Recuerdaquelamacrovec!seutilizaenuncontextodeexpresión.Paraescribirunaexpresiónconvariassentencias,entreellasenlacesavariable,utilizamosunbloque.Silamacroseexpandeaunasolaexpresión,nonecesitasdichacapaextradellaves.

Hayquetenertambiénencuentaquenuncadeclaramosquelamacroproduceunaexpresión.Dehecho,estonosedeterminahastaqueusamoslamacrocomounaexpresión.Concuidado,sepuedeescribirunamacrocuyaexpansiónfuncioneenvarioscontextos.Porejemplo,laabreviaturadeuntipodedatospodríaserválidacomounaexpresiónounpatrón.

RepeticiónEloperadorderepeticiónsiguedosreglasprincipales:

ElLenguajedeProgramacionRust

297Macros

1. $(...)*caminaatravésdeuna"capa"derepeticiones,paratodoslos$nombresquecontiene,almismopaso,y

2. cada$nombredebeestarbajoalmenostantos$(...)*comolosquefuecomparado.Siestabajomás,seraduplicado,segúnelcaso.

Estamacrobarrocailustraladuplicacióndelasvariablesdelosnivelesderepeticiónexteriores.

macro_rules!o_O{

(

$(

$x:expr;[$($y:expr),*]

);*

)=>{

&[$($($x+$y),*),*]

}

}

fnmain(){

leta:&[i32]

=o_O!(10;[1,2,3];

20;[4,5,6]);

assert_eq!(a,[11,12,13,24,25,26]);

}

Esaeslamayorpartedelasintaxisdelosmatcher.Todoslosejemplosutilizan$(...)*,quecoincidecon"ceroomás"elementossintácticos.Alternativamente,puedesescribir$(...)+quecoincidecon"unoomás".Ambasformasincluyen,opcionalmente,unseparador,quepuedesercualquiertokenexcepto+o*.

Estesistemasebasaen"Macro-by-Example"(PDF).

HigieneAlgunoslenguajesimplementanmacrosconsustitucióndetextosimple,loquetraecomoconsecuenciadiversosproblemas.Porejemplo,esteprogramaCimprime13enlugardelaesperada25.

#defineCINCO_VECES(x)5*x

intmain(){

printf("%d\n",CINCO_VECES(2+3));

return0;

}

ElLenguajedeProgramacionRust

298Macros

Despuésdelaexpansióntenemos5*2+3,endondelamultiplicacióntienemayorprecedenciaquelasuma.SihasutilizadomuchosmacrosenC,probablementeconoceslosidiomasestándarparaevitaresteproblema,asícomocincooseisotros.EnRust,nonospreocupamosporello.

macro_rules!cinco_veces{

($x:expr)=>(5*$x);

}

fnmain(){

assert_eq!(25,cinco_veces!(2+3));

}

Lametavariable$xseanalizacomounnododeexpresiónindividual,ymantienesulugarenelárboldesintaxis,inclusodespuésdelasustitución.

Otroproblemacomúnenlossistemasdemacroesla"capturadevariable".AquíhayunamacroC,utilizandounaextensióndeGNUCparaemularbloquesdeexpresióndeRust.

#defineLOG(msj)({\

intestado=obtener_estado_log();\

if(estado>0){\

printf("log(%d):%s\n",estado,msj);\

}\

})

Heaquíuncasodeusoquevaterriblementemal:

constchar*estado="estríasreticuladas";

LOG(estado)

Loanteriorseexpandea

constchar*estado="estríasreticuladas";

intestado=obtener_estado_log();

if(estado>0){

printf("log(%d):%s\n",estado,estado);

}

Lasegundavariablellamadaestadosobreescribealaprimera.Estoesunproblemaporquelaexpresióndeimpresión(printf)debehacerreferenciaaambas.

LamacroRustequivalentetieneelcomportamientodeseado.

ElLenguajedeProgramacionRust

299Macros

#fnobtener_estado_log()->i32{3}

macro_rules!log{

($msj:expr)=>{{

letestado:i32=obtener_estado_log();

ifestado>0{

println!("log({}):{}",estado,$msj);

}

}};

}

fnmain(){

letestado:&str="estríasreticuladas";

log!(estado);

}

EstaversiónfuncionaporqueRusttieneunsistemademacroshigiénico.Cadaexpansióndemacroocurreenun"contextodesintaxis"distinto,ycadavariableestáasociadaconelcontextodesintaxisdondefueintroducida.Escomosilavariableestadodentromainestápintadodeun"color"diferentedelavariableestadodentrodelamacro,yporlotantonoentranenconflicto.

ElsistemademacrosdeRusttambiénrestringelacapacidaddelasmacrosparaintroducirnuevosenlacesenelsitiodeinvocación.Códigocomoelsiguientenofuncionará:

macro_rules!foo{

()=>(letx=3);

}

fnmain(){

foo!();

println!("{}",x);

}

Enlugardeesonecesitaspasarelnombredelavariableenlainvocación,demaneraqueseaestiquetadoconelcontextodesintaxiscorrecto.

macro_rules!foo{

($v:ident)=>(let$v=3);

}

fnmain(){

foo!(x);

println!("{}",x);

}

ElLenguajedeProgramacionRust

300Macros

Loanterioresválidoparaenlacesletyetiquetasdebucle,peronoparaitems.Asíqueelsiguientecódigocompila:

macro_rules!foo{

()=>(fnx(){});

}

fnmain(){

foo!();

x();

}

MacrosrecursivasLaexpansióndeunamacropuedeincluirmásinvocacionesamacro,incluyendoinvocacionesdelamismamacroqueestasiendoexpandida.Estasmacrosrecursivassonútilesparaelprocesamientodeentradaconestructuradeárbol,comoseilustraenesta(simplista)taquigrafíaHTML:

ElLenguajedeProgramacionRust

301Macros

##![allow(unused_must_use)]

macro_rules!write_html{

($w:expr,)=>(());

($w:expr,$e:tt)=>(write!($w,"{}",$e));

($w:expr,$tag:ident[$($inner:tt)*]$($rest:tt)*)=>{{

write!($w,"<{}>",stringify!($tag));

write_html!($w,$($inner)*);

write!($w,"</{}>",stringify!($tag));

write_html!($w,$($rest)*);

}};

}

fnmain(){

#//FIXME(#21826)

usestd::fmt::Write;

letmutout=String::new();

write_html!(&mutout,

html[

head[title["Macrosguide"]]

body[h1["Macrosarethebest!"]]

]);

assert_eq!(out,

"<html><head><title>Macrosguide</title></head>\

<body><h1>Macrosarethebest!</h1></body></html>");

}

DepurandocódigodemacroParaverlosresultadosdelasmacrosenexpansión,ejecutarustc--prettyexpanded.Lasalidarepresentatodouncrate,porloquetambiénpuedealimentardenuevoarustc,queasuvezproducirámejoresmensajesdeerrorquelacompilacióninicial.Esimportantedestacarquelasalidade--prettyexpandedpuedetenerunsignificadodiferentesivariasvariablesdelmismonombre(perodiferentescontextossintácticos)estánenjuegoenelmismoámbito.Enestecaso--prettyexpanded,hygienetediráacercadeloscontextosdesintaxis.

rustcofrecedosextensionesdesintaxisqueayudanconladepuracióndemacros.Porahora,soninestablesyrequierenpuertasdecaracterísticas(featuregates).

log_syntax!(...)imprimirásusargumentosenlasalidaestándar,entiempodecompilación,yse"expandirá"anada.

ElLenguajedeProgramacionRust

302Macros

trace_macros!(true)habilitaraunmensajecompiladorcadavezqueunamacroesexpandida.Usetrace_macros!(false)adelanteenlaexpansiónparaapagarlo.

MasinformaciónElcapituloavanzadodemacrosentraenmasdetallesacercadelasintaxisdemacros.Tambiéndescribecomocompartirmacrosentrediferentescratesymódulos.

ElLenguajedeProgramacionRust

303Macros

%ApuntadoresPlanos

ElLenguajedeProgramacionRust

304ApuntadoresPlanos

%Unsafe

LaprincipalatraccióndeRustsonsuspoderosasgarantíasestáticasacercadecomportamiento.Peroloschequeosdeseguridadsonconservadorespornaturaleza:existenprogramasquesonenefectoseguros,peroelcompiladornoescapazdedeverificarqueestoseacierto.Paraescribiresetipodeprogramas,debemosdecirlealcompiladorquerelajeunpocosusrestricciones.Paraello,Rustposeeunapalabrareservada,unsafe.Elcódigoquehaceusodeunsafeposeemenosrestriccionesqueelcódigonormal.

Repasemoslasintaxis,yluegohablaremosdelasemántica.unsafeesusadoencuatrocontextos.Elprimeroesparamarcarunafuncióncomoinsegura:

unsafefnpeligro_will_robinson(){

//cosaspeligrosas

}

TodaslasfuncionesllamadasdesdeFFIdebensermarcadascomounsafe,porejemplo.Elsegundousodeunsafeesunbloqueunsafe:

unsafe{

//osaspeligrosas

}

Elterceroesparatraitsunsafe:

unsafetraitPeligroso{}

Ylacuartaesparalaimplementacióndeunodedichostraits:

#unsafetraitPeligroso{}

unsafeimplPeligrosofori32{}

Esimportantepodertenerlacapacidaddedelinearcódigoquepodríaposiblementecontenerbugsqueoriginenproblemasgraves.SiunprogramaRustterminademaneraabrupta(unsegfault),puedestenerporseguroqueesenalgúnlugardelasseccionesmarcadascomounsafe.

Quesignifica‘seguro’?

ElLenguajedeProgramacionRust

305`unsafe`

Seguro,enelcontextodeRust,significa‘nohacernadainseguro’.Esimportantesaberquehayciertoscomportamientosquesonprobablementeindeseablesentucódigo,perosonexpresamentenoinseguros:

Deadlocks.Perdidadememoriauotrosrecursos.Salidasinllamadaalosdestructores.Desbordamientodeenteros.

Rustnopuedeprevenirtodoslostiposdeproblemasdesoftware.Lascosasdelalistaanteriornosonbuenas,perotampococalificancomounsafeespecíficamente.

Enadición,lossiguientessontodoscomportamientoindefinidoenRust,ydebenserevitadas,inclusocuandoseescribecódigounsafe:

Condicionesdecarrera.Deereferenciarunapuntadornulo/colgante.Lecturadememoriaundef(memorianoinicializada)Violacióndelasreglasdealiasingdeapuntadoresatravésdeapuntadoresplanos.&mutTy&TsiguenelmodelonoaliasdeLLVM,exceptocuandoel&TcontieneunUnsafeCell<U>.Elcódigounsafenodebeviolaresasgarantíasdealiasing.Mutarunvalor/referenciainmutablesinunUnsafeCell<U>.Invocarcomportamientoindefinidoatravésdeintrínsecosdelcompilador:

Indexarporfueradeloslimitesdeunobjetoconstd::ptr::offset(intrínsecooffset),conlaexcepcióndeunsolobytedespuésdelfinallocualespermitido.Usarstd::ptr::copy_nonoverlapping_memory(intrínsecosmemcpy32/memcpy64)enbuffersquesesolapen.

Valoresinválidosentiposprimitivos,inclusoencampos/variableslocalesprivadas:Referenciasnull,referenciascolgantesoboxes.Unvalordistintoquefalse(0)otrue(1)enunbool.Undiscriminanteenunenumquenoesteincluidoensudefinicióndetipo.Unvalorenuncharelcualesunsustitutooporencimadechar::MAX.Unasecuenciadebytesno-UTFenunstr.

UnwindingenRustdesdecódigoforaneoounwindingdesdeRustacódigoforaneo.

SuperpoderesUnsafeEnambosfuncionesybloquesunsafe,Rusttepermitiráhacertrescosasquenormalmentenopodríashacer.Solotres.Yson:

1. Accederoactualizarunavariablemutableestética.

ElLenguajedeProgramacionRust

306`unsafe`

2. Dereferenciarunapuntadorplano.3. Llamarafuncionesunsafe.Estaeslahabilidadmasimportante.

Esoestodo.Esimportantedestacarqueunsafe,porejemplo,no‘apagaelcomprobadordeprestamos’.Agregardemaneraaleatoriaunsafeaalgúncódigonocambiasusemántica,nocomenzaraaaceptaralgo.Perotepermitiráescribircosasquesirompenalgunasdelasreglas.

Tambiénencontraraslapalabrareservadaunsafecuandoescribasbindingsainterfacesforáneas(no-Rust).LomasrecomendableesescribirunasegurainterfaznativaenRustalrededordelosmétodosproporcionadosporlalibrería.

Echemosunvistazoalastreshabilidadeslistadas,enorden.

AccederoactualizarunastaticmutRustposeeunafacilidaddenominada‘staticmut’quetepermitehacerestadoglobalmutable.Hacerlopuedecausarunacondicióndecarrera,yenconsecuenciaesinherentementeinseguro.Paramayordetalle,dirígetealasecciónstaticdellibro.

DereferenciarunapuntadorplanoLosapuntadoresplanostepermitenllevaracaboaritméticadepunterosarbitraria,ypuedencausarunnumerodeproblemasdeseguridad.Enalgunossentidos,lahabilidaddedereferenciarunapuntadorarbitrarioesunadelascosasmaspeligrosasquepuedeshacer,masinformaciónensusecciónenellibro.

LlamarfuncionesunsafeEstaultimahabilidadfuncionaconambosaspectosdeunsafe:puedessolollamarafuncionesmarcadascomounsafedesdedentrodeunbloqueunsafe.

Estahabilidadespoderosayvariada.Rustexponealgunosintrínsecosdelcompiladorcomofuncionesunsafe,yalgunasfuncionesunsafehacenbypassdealgunoschequeosdeseguridad,intercambiandoseguridadporvelocidad.

Lorepetirédenuevo:aunycuandopuedeshacercosasarbitrariasenbloquesunsafeyfuncionesnosignificaquedebashacerlo.Elcompiladoractuaracomosituereselresponsableestuviesesdemantenerarribatodaslasinvariantes,asíquedebessercuidadoso!

ElLenguajedeProgramacionRust

307`unsafe`

%RustNocturno

Rustproporcionatrescanalesdedistribución:nocturno(nightly),betayestable(stable).LasfacilidadesinestablesestánsolodisponiblesenRustnocturno.Paramayordetalleacercadeesteproceso,véase‘Estabilidadcomounentregable’.

ParainstalarRustnocturno,puedesusarrustup.sh:

$curl-shttps://static.rust-lang.org/rustup.sh|sh-s----channel=nightly

Sitienesalgunadudaacercadela[inseguridadpotencial]delusodecurl|sh,porfavor,continualeyendoyencontrarasnuestrodisclaimer.Siéntetetambiénenlibertaddeusarunaversiondedospasosdelainstalaciónexaminandonuestroscript:

$curl-f-Lhttps://static.rust-lang.org/rustup.sh-O

$shrustup.sh--channel=nightly

SiestasenWindows,porfavordescargabienseaelinstaladorde32bitsoelinstaladorde64bitsyejecutalo.

DesinstalandoSihasdecididoqueyanoquieresmasaRust,estaremosunpocotriste,peroestabien.Notodosloslenguajesdeprogramaciónsonparatodoelmundo.Simplementeejecutaelscriptdedesinstalación:

$sudo/usr/local/lib/rustlib/uninstall.sh

SiusasteelinstaladordeWindows,simplementeejecutael.msidenuevoyestetedarálaopcióndedesinstalación.

Algunaspersonas,ydealgunaformaconderecho,sesientenperturbadoscuandolesdecimosquehagancurl|sh.Básicamente,cuandohacesesto,estasconfiandoquelabuenagentequemantieneRustnovanahackeartucomputadorayhacercosasmalas.Esoes=bueninstinto!Sieresunadeesaspersonas,porfavorechaunvistazoaladocumentaciónacercadelainstalacióndeRustdesdefuentes.Olasdescargasdelosejecutablesoficiales.

Ah,debemosmencionarlasplataformasoficialmentesoportadas:

Windows(7,8,Server2008R2)Linux(2.6.18omayor,variasdistribuciones),x86andx86-64

ElLenguajedeProgramacionRust

308RustNocturno

OSX10.7(Lion)omayor,x86andx86-64

Nosotros,elequipodeRustprobamosRustdemaneraextensivaenesasplataformas,yotraspocasmas,comoAndroid.Peroestassonlasmaspropensasenfuncionarcorrectamente,debidoaquesonlasqueposeenmaspruebas.

Finalmente,uncomentarioacercadeWindows.RustconsideraaWindowscomounaplataformadeprimeraclaseenloqueserefierealrelease,perosisomoshonestos,laexperienciaenWindowsnoestatanintegradacomolasexperienciasenLinuxyOSX.Estamostrabajandoenello!Sialgonofunciona,esunbug.Porfavorhaznossabersiesopasa.CadacommmitesprobadoenWindowsjustocomocualquierotraplataforma.

SihasinstaladoRust,puedesabrirunaterminal,yescribir:

$rustc--version

Deberíasverelnumerodeversion,elhashdecommit,fechadelcommityfechadecompilación:

rustc1.0.0-nightly(f11f3e7ba2015-01-04)(built2015-01-06)

Siloves,Rusthasidoinstaladosatisfactoriamente!Felicidades!

Esteinstaladortambiéninstalaunacopialocaldeladocumentación,demaneraquepuedasleerlaoffline.EnsistemasUNIX,lalocaciónes/usr/local/share/doc/rust.Enwindows,seubicaeneldirectorioshare/doc,endondeseaquehayasinstaladoRust.

Sino,hayunavariedaddelugaresenlosquepuedessolicitarayuda.Elmasfácileselcanal#rustenirc.mozilla.org,elcualpuedesaccederviaMibbit.Cliqueaeseenlace,yestaráschateandoconRutaceos(untontoapodoconelquenosreferimosanosotros),ypodremosayudarte.Otrosmuybuenosrecursosincluyenelforodeusuarios,yStackOverflow.

ElLenguajedeProgramacionRust

309RustNocturno

%PluginsdelCompilador

Introducciónrustcpuedecargarpluginsdecompilador,quesonbibliotecasdeusuarioqueextiendenelcomportamientodelcompiladorconnuevasextensionesdesintaxis,lints,etc.

Unpluginpuedeserunabibliotecadinámicaconunafunciónregistradoradesignadaseencargaregistrarlaextensionconrustc.Otroscratespuedencargarestasextensionesatravésdelusodelatributo#![plugin(...)].Dirígetealadocumentaciónderustc_pluginparamayordetalleacercadelamecánicadeladefiniciónycargadeunplugin.

Deestarpresentes,losargumentospasadoscomo#![plugin(foo(...args...))]nosoninterpretadosporrustc.SonproporcionadosalpluginatravésdelmétodoargsdeRegistry.

Enlagranmayoríadeloscasos,unpluginsolodeberíaserusadoatravésde#![plugin]ynopormediodeunitemexterncrate.Enlazarunplugintraeríaalibsyntaxylibrustccomodependenciasdetucrate.Estoesgeneralmenteindeseableamenosqueestésconstruyendounplugin.Ellintplugin_as_librarychequeaestoslineamientos.

Lapracticausualescolocaralospluginsdelcompiladorensupropiocrate,separadosdecualquiermacromacro_rules!ocódigoRustordinarioquevayanaserusadosporlosconsumidoresdelabiblioteca.

ExtensionesdesintaxisLospluginspuedenextenderlasintaxisdeRustdevariasmaneras.Unaformadeextensiondesintaxissonlasmacrosprocedurales.Estassoninvocadasdelamismaformaquelasmacrosordinarias,perolaexpansiónesllevadaacaboporcódigoRustarbitrarioquemanipulaarbolesdesintaxisentiempodecompilación.

Escribamosunpluginroman_numerals.rsqueimplementaliteralesdeenterosconnúmerosromanos.

#![crate_type="dylib"]

#![feature(plugin_registrar,rustc_private)]

externcratesyntax;

externcraterustc;

externcraterustc_plugin;

ElLenguajedeProgramacionRust

310PluginsdelCompilador

usesyntax::codemap::Span;

usesyntax::parse::token;

usesyntax::ast::TokenTree;

usesyntax::ext::base::{ExtCtxt,MacResult,DummyResult,MacEager};

usesyntax::ext::build::AstBuilder;//traitparaexpr_usize

userustc_plugin::Registry;

fnexpand_rn(cx:&mutExtCtxt,sp:Span,args:&[TokenTree])

->Box{

staticNUMERALS:&'static[(&'staticstr,usize)]=&[

("M",1000),("CM",900),("D",500),("CD",400),

("C",100),("XC",90),("L",50),("XL",40),

("X",10),("IX",9),("V",5),("IV",4),

("I",1)];

ifargs.len()!=1{

cx.span_err(

sp,

&format!("argumentshouldbeasingleidentifier,butgot{}arguments",args.len()));

returnDummyResult::any(sp);

}

lettext=matchargs[0]{

TokenTree::Token(_,token::Ident(s,_))=>s.to_string(),

_=>{

cx.span_err(sp,"argumentshouldbeasingleidentifier");

returnDummyResult::any(sp);

}

};

letmuttext=&*text;

letmuttotal=0;

while!text.is_empty(){

matchNUMERALS.iter().find(|&&(rn,_)|text.starts_with(rn)){

Some(&(rn,val))=>{

total+=val;

text=&text[rn.len()..];

}

None=>{

cx.span_err(sp,"invalidRomannumeral");

returnDummyResult::any(sp);

}

}

}

MacEager::expr(cx.expr_usize(sp,total))

}

#[plugin_registrar]

pubfnplugin_registrar(reg:&mutRegistry){

reg.register_macro("rn",expand_rn);

}

ElLenguajedeProgramacionRust

311PluginsdelCompilador

Entoncespodemoshacerusodern!()comocualquierotramacro:

#![feature(plugin)]

#![plugin(roman_numerals)]

fnmain(){

assert_eq!(rn!(MMXV),2015);

}

Lasventajasdeestoporencimadeunasimplefn(&str)->u32son:

Laconversión(arbitrariamentecompleja)esefectuadaentiempodecompilación.Lavalidacióndeentradaestambiénllevadaacaboentiempodecompilación.Puedeserextendidaparasuusoenpatrones,locaulefectivamenteproporcionaunaformadedefinirunasintaxisliteralnuevaparacualquiertipodedato.

Enadicionalasmacrosprocedurales,puedesdefinirattributosderiveyotrostiposdeexpansiones.DirigeteaRegistry::register_syntax_extensionyaelenumSyntaxExtension.Paraunejemplomasinvolucradoconmacros,vearegex_macros.

TipsytrucosAlgunosdelostipsdedepuracióndemacrossonaplicables.

Puedesusarsyntax::parseparatransformararbolesdetokensenelementosdesintaxisdemasaltonivel,comoexpresiones:

fnexpandir_foo(cx:&mutExtCtxt,sp:Span,args:&[TokenTree])

->Box{

letmutparser=cx.new_parser_from_tts(args);

letexpr:P=parser.parse_expr();

Miraraelcódigodelparserlibsyntaxtedaraunaideadecomofuncionalainfraestructuradeparseo.

ManténlosSpansdetodoloqueparsees,paraunmejorreportedeerrores.PuedesenvolverSpannedalrededordetusestructurasdedatos,

ElLenguajedeProgramacionRust

312PluginsdelCompilador

LlamaraExtCtxt::span_fatalabortarademanerainmediatalacompilación.EsmejorllamaraExtCtxt::span_errretornandounDummyResult,demaneraqueelcompiladorpuedacontinuarencontrandomaserrores.

Paraimprimirfragmentosdesintaxisparadepuración,pudesusarspan_noteenconjunciónconsyntax::print::pprust::*_to_string.

ElejemploanteriorprodujounliteraldeenterousandoAstBuilder::expr_usize.ComoalternativaaeltraitAstBuilder,libsyntaxproporcionaunconjuntodemacrosquasiquote.Estosestánindocumentadosyunpocorústicosenlosbordes.Sinembargo,laimplementaciónpuedeserunbuenpuntodepartidaparaunquasiquotemejoradocomounabibliotecapluginordinaria.

PluginslintLospluginspuedenextenderlainfraestructuradelintsdeRustconchequeosadicionalesparaestilodecódigo,seguridad,etc.Escribamosahoraunpluginlint_plugin_test.rsquenosadvierteacercadecualquieritemllamadolintme.

ElLenguajedeProgramacionRust

313PluginsdelCompilador

#![feature(plugin_registrar)]

#![feature(box_syntax,rustc_private)]

externcratesyntax;

//Cargandorustccomounpluginparaobtenerlasmacros

#[macro_use]

externcraterustc;

externcraterustc_plugin;

userustc::lint::{EarlyContext,LintContext,LintPass,EarlyLintPass,

EarlyLintPassObject,LintArray};

userustc_plugin::Registry;

usesyntax::ast;

declare_lint!(TEST_LINT,Warn,"Warnaboutitemsnamed'lintme'");

structPass;

implLintPassforPass{

fnget_lints(&self)->LintArray{

lint_array!(TEST_LINT)

}

}

implEarlyLintPassforPass{

fncheck_item(&mutself,cx:&EarlyContext,it:&ast::Item){

ifit.ident.name.as_str()=="lintme"{

cx.span_lint(TEST_LINT,it.span,"itemisnamed'lintme'");

}

}

}

#[plugin_registrar]

pubfnplugin_registrar(reg:&mutRegistry){

reg.register_early_lint_pass(boxPassasEarlyLintPassObject);

}

Entoncescódigocomo

#![plugin(lint_plugin_test)]

fnlintme(){}

produciráunaadvertenciadelcompilador:

foo.rs:4:1:4:16warning:itemisnamed'lintme',#[warn(test_lint)]onbydefault

foo.rs:4fnlintme(){}

^~~~~~~~~~~~~~~

ElLenguajedeProgramacionRust

314PluginsdelCompilador

Loscomponentesdeunpluginlintson:

unaomasinvocacionesdeclare_lint!,lascualesdefinesstructsLint.

unstructmanteniendoelestadonecesarioparaelpasslint(aquí,ninguno);

unaimplementaciónLintPassdefiniendocomochequearcadaelementodesintaxis.UnúnicoLintPasspuedellamaraspan_lintparadiferentesLints,perodeberegistrarlosatodosatravésdelmétodoget_lints.

Lospasseslintsonrecorridosdesintaxis,perocorrenenunaetapadelacompilaciónenlaquelainformacióndetiposestadisponible.LoslintsintegradosdeRustusanmayormentelainfraestructuradelospluginslint,yproveenejemplosdecomoaccederalainformacióndetipos.

Loslintsdefinidosporpluginssoncontroladosporlosflagsyatributosusualesdelcompilador,e.j#[allow(test_lint)]o-Atest-lint.estosidentificadoressonderivadosdelprimerargumentosadeclare_lint!,conlasconversionesdecapitalizaciónypuntuaciónapropiadas.

Puedesejecutarrustc-Whelpfoo.rsparaverunalistadeloslintsquerustcconoce,incluyendoaquellosproporcionadosporpluginscargadosporfoo.rs.

ElLenguajedeProgramacionRust

315PluginsdelCompilador

%Ensambladorenlinea

Paramanipulacionesdemuybajonivelyporrazonesdedesempeño,unopodríadesearcontrolarlaCPUdemaneradirecta.ParaestoRustsoportaelusodeensambladorenlineaatravésdelamacroasm!.LasintaxisesparecidaaladeGCC&Clang:

asm!(plantillaensamblador

:operandosdesalida

:operandosdeentrada

:clobbers

:opciones

);

Cualquierusodeasmestaprotegidoporpuertasdefeature(requiere#![feature(asm)]enelcrate)yporsupuestorequieredeunbloqueunsafe.

Nota:losejemplosproporcionadosaquíestánescritosenensambladorx86/x86-64,perotodaslasplataformasestánsoportadas.

PlantillaensambladorLaplantillaensambladoreselúnicoparámetromandatorioydebeserunliteraldecadenadecaracteres(e.j."")

#![feature(asm)]

#[cfg(any(target_arch="x86",target_arch="x86_64"))]

fnfoo(){

unsafe{

asm!("NOP");

}

}

//otrasplataformas

#[cfg(not(any(target_arch="x86",target_arch="x86_64")))]

fnfoo(){/*...*/}

fnmain(){

//...

foo();

//...

}

(Losfeature(asm)y#[cfg]ssonomitidosdeahoraenadelante.)

ElLenguajedeProgramacionRust

316EnsambladorenLinea

Losoperandosdesalida,operandosdeentrada,clobbersyopcionessontodosopcionalesperodebesagregarelnumerocorrectode:encasodequedeseessaltarlos:

##![feature(asm)]

##[cfg(any(target_arch="x86",target_arch="x86_64"))]

#fnmain(){unsafe{

asm!("xor%eax,%eax"

:

:

:"{eax}"

);

#}}

Losespaciosenblanconosonsignificativos:

##![feature(asm)]

##[cfg(any(target_arch="x86",target_arch="x86_64"))]

#fnmain(){unsafe{

asm!("xor%eax,%eax":::"{eax}");

#}}

OperandosLosoperandosdeentradaysalidasiguenelmismoformato:restriccion1"(expr1),"restriccion2"(expr2),...".Lasexpresionesdeoperandosdesalidadebenservaloreslvaluemutables,ovaloresaunnoasignados:

##![feature(asm)]

##[cfg(any(target_arch="x86",target_arch="x86_64"))]

fnadd(a:i32,b:i32)->i32{

letc:i32;

unsafe{

asm!("add$2,$0"

:"=r"(c)

:"0"(a),"r"(b)

);

}

c

}

##[cfg(not(any(target_arch="x86",target_arch="x86_64")))]

#fnadd(a:i32,b:i32)->i32{a+b}

fnmain(){

assert_eq!(add(3,14159),14162)

}

ElLenguajedeProgramacionRust

317EnsambladorenLinea

Sinembargo,Sidesearasusaroperandosrealesenestaposición,seriaobligatoriocolocarllaves{}alrededordelregistroquedeseas,ytambiénestaríasobligadoacolocareltamañoespecificodeloperando.Loanterioresmuyutilparaprogramacióndemuybajonivel,paralaqueesimportantecualregistroeselqueseausa.

##![feature(asm)]

##[cfg(any(target_arch="x86",target_arch="x86_64"))]

#unsafefnread_byte_in(puerto:u16)->u8{

letresultado:u8;

asm!("in%dx,%al":"={al}"(resultado):"{dx}"(puerto));

resultado

#}

ClobbersAlgunasinstruccionesmodificanregistrosloscualesdeotraformahubieranmantenidovaloresdiferentesesporelloqueusamoslaslistadeclobbersparaindicaralcompiladoraquenoasumaqueningúnvalorcargadoendichosregistrossemantendrávalido.

##![feature(asm)]

##[cfg(any(target_arch="x86",target_arch="x86_64"))]

#fnmain(){unsafe{

//Colocandoelvalor0x200eneax

asm!("mov$$0x200,%eax":/*sinsalidas*/:/*sinentradas*/:"{eax}");

#}}

Losregistrosdeentradaysalidanonecesitanserlistadospuestoaqueesainformaciónyaestacomunicadaporlasdeterminadasrestricciones.Delocontrario,cualquierotroregistrousadoyaseademaneraexplicitaoimplícitadebeserlistado.

Sielensambladorcambiaelregistrodecódigodecondiciónccdebeserespecificadocomounodelosclobbers.Similarmente,sielensambladormodificamemoria,memorydebesertambiénespecificado.

OpcionesLaultimasección,opcionesesespecíficadeRust.Elformatoesunalistadecadenasdecaracteresseparadasporcomma(e.j::"foo","bar","baz").Esusadoparaespecificarinformaciónextraacercadelensamblador:

Lasopcionesvalidasactualmenteson:

ElLenguajedeProgramacionRust

318EnsambladorenLinea

1. volatile-especificarestoesanalogoa__asm____volatile__(...)engcc/clang.2. alignstack-ciertasinstruccionesesperanquelapilaestealineadadeciertamanera

(e.j.SSE),especificarestoleindicaalcompiladorqueinsertesucódigodealineamientodepilausual.

3. intel-usodelasintaxisdeintelenlugardelaAT&T.

##![feature(asm)]

##[cfg(any(target_arch="x86",target_arch="x86_64"))]

#fnmain(){

letresultado:i32;

unsafe{

asm!("moveax,2":"={eax}"(resultado):::"intel")

}

println!("eaxesactualmente{}",resultado);

#}

MasInformaciónLaimplementaciónactualdelamacroasm!esunbindingdirectoalasexpresionesensambladorenlineadeLLVM,asíqueaseguratedeecharleunvistazoasudocumentaciónparamayorinformaciónacercadeclobbers,restricciones,etc.

ElLenguajedeProgramacionRust

319EnsambladorenLinea

%Nostdlib

ElLenguajedeProgramacionRust

320Nostdlib

%Intrínsecos

Nota:losintrínsecosporsiempretendránunainterfazinestable,serecomiendausarlasinterfacesestablesdelibcoreenlugardeintrínsecosdirectamente.

EstassonimportadascomosifuesenfuncionesFFI,conelABIespecialrust-intrinsic.Porejemplo,siunoestuvieseenuncontextolibre,perodeseasepoderhacertransmuteentretipos,yllevaracaboaritméticadeapuntadoreseficiente,unoimportaríadichasfuncionesviaunadeclaracióncomo:

#![feature(intrinsics)]

#fnmain(){}

extern"rust-intrinsic"{

fntransmute<T,U>(x:T)->U;

fnoffset<T>(dst:*constT,offset:isize)->*constT;

}

AligualquecualquierotrafunciónFFI,estassonsiempreunsafedellamar.

ElLenguajedeProgramacionRust

321Intrínsecos

%ItemsdeLenguaje

ElLenguajedeProgramacionRust

322ItemsdeLenguaje

%EnlaceAvanzado

LoscasoscomunesdeenlaceconRusthansidocubiertosanteriormenteenestelibro,perosoportarelrangodeposibilidadesdisponiblesenlenguajesesimportanteparaRustparalograrunabuenainteracciónconbibliotecasnativas.

ArgumentosdeenlaceHayotraformadedecirlearustccomopersonalizarelenlazado,yesviaelatributolink_args.Esteatributoesaplicadoalosbloquesexternyespecificaflagsplanosquenecesitanserpasadosaelenlazadorcuandoseproduceunartefacto.Unejemplopodríaser:

#![feature(link_args)]

#[link_args="-foo-bar-baz"]

extern{}

#fnmain(){}

Notaqueactualmenteestafacilidadestaescondidadetráslapuertafeature(link_args)debidoaquenoesunaformasancionadadeefectuarenlazado.Actualmenterutscenvuelveaelenlazadordelsistema(gccenlamayoríadelossistemas,link.exeenMSVC),esporelloquetienesentidoproveerargumentosdelineadecomandoextra,peronosiempreseraesteelcaso.EnelfuturorustcpudierausarLLVMdirectamenteparaenlazarconbibliotecasnativas,escenarioendondelink_argsnotendríasentido.Puedeslograrelmismoefectodelatributolink_argsconelargumento-Clink-argsderustc.

Esaltamenterecomendadonousaresteatributo,yensulugarusarelmasformal#[link(...)]enlosbloquesextern.

EnlazadoestaticoElenlaceestáticoserefiereaelprocesodecrearunasalidaquecontengatodaslasbibliotecasrequeridasyporlotantonorequieraquelasbibliotecasesténinstaladasencadasistemaenelquedeseesusartuproyectocompilado.LadependenciaspurasRustsonenlazadasestáticamentepordefectodemaneraquepuedasusarlasbibliotecasyejecutablescreadossininstalarRustentodoslados.Encontraste,lasbibliotecasnativas(e.jlibcylibm)sonusualmenteenlazadasdemaneradinámica,peroestosepuedecambiaryenlazarlastambiéndemaneraestática.

ElLenguajedeProgramacionRust

323EnlaceAvanzado

Elenlaceesuntópicomuydependientedeplataforma,yelenlaceestáticopuedenoserposibleentodaslasplataformas.Estasecciónasumeciertafamiliaridadconelenlaceentuplataforma.

LinuxPordefecto,todoslosprogramasenLinuxseenlazaranconlabibliotecalibcdelsistemaenconjuntoconotrogrupodebibliotecas.Veamosunejemploenunamaquinalinuxde64bitsconGCCyglibc(porlejoslalibcmascomúnenLinux):

$catexample.rs

fnmain(){}

$rustcexample.rs

$lddexample

linux-vdso.so.1=>(0x00007ffd565fd000)

libdl.so.2=>/lib/x86_64-linux-gnu/libdl.so.2(0x00007fa81889c000)

libpthread.so.0=>/lib/x86_64-linux-gnu/libpthread.so.0(0x00007fa81867e000)

librt.so.1=>/lib/x86_64-linux-gnu/librt.so.1(0x00007fa818475000)

libgcc_s.so.1=>/lib/x86_64-linux-gnu/libgcc_s.so.1(0x00007fa81825f000)

libc.so.6=>/lib/x86_64-linux-gnu/libc.so.6(0x00007fa817e9a000)

/lib64/ld-linux-x86-64.so.2(0x00007fa818cf9000)

libm.so.6=>/lib/x86_64-linux-gnu/libm.so.6(0x00007fa817b93000)

Elenlacedinámicoenlinuxpuedeserindeseablesideseasusarlasfacilidadesdelanuevabibliotecaensistemasviejososistemasdestinoquenoposeenlasdependenciasrequeridasparaejecutarelprograma.

Elenlaceestáticoessoportadoatravésdeunalibcalternativa,musl.PuedescompilartuversiondeRustconmuslhabilitadoeinstalarloenundirectoriopersonalizadoconlassiguientesinstrucciones:

ElLenguajedeProgramacionRust

324EnlaceAvanzado

$mkdirmusldist

$PREFIX=$(pwd)/musldist

$

$#Construirmusl

$curl-Ohttp://www.musl-libc.org/releases/musl-1.1.10.tar.gz

$tarxfmusl-1.1.10.tar.gz

$cdmusl-1.1.10/

musl-1.1.10$./configure--disable-shared--prefix=$PREFIX

musl-1.1.10$make

musl-1.1.10$makeinstall

musl-1.1.10$cd..

$du-hmusldist/lib/libc.a

2.2Mmusldist/lib/libc.a

$

$#Construirlibunwind.a

$curl-Ohttp://llvm.org/releases/3.7.0/llvm-3.7.0.src.tar.xz

$tarxfllvm-3.7.0.src.tar.xz

$cdllvm-3.7.0.src/projects/

llvm-3.7.0.src/projects$curlhttp://llvm.org/releases/3.7.0/libunwind-3.7.0.src.tar.xz|tarxJf-

llvm-3.7.0.src/projects$mvlibunwind-3.7.0.srclibunwind

llvm-3.7.0.src/projects$mkdirlibunwind/build

llvm-3.7.0.src/projects$cdlibunwind/build

llvm-3.7.0.src/projects/libunwind/build$cmake-DLLVM_PATH=../../..-DLIBUNWIND_ENABLE_SHARED=0..

llvm-3.7.0.src/projects/libunwind/build$make

llvm-3.7.0.src/projects/libunwind/build$cplib/libunwind.a$PREFIX/lib/

llvm-3.7.0.src/projects/libunwind/build$cd../../../../

$du-hmusldist/lib/libunwind.a

164Kmusldist/lib/libunwind.a

$

$#ConstruirRustconmuslhabilitado

$gitclonehttps://github.com/rust-lang/rust.gitmuslrust

$cdmuslrust

muslrust$./configure--target=x86_64-unknown-linux-musl--musl-root=$PREFIX--prefix=$PREFIX

muslrust$make

muslrust$makeinstall

muslrust$cd..

$du-hmusldist/bin/rustc

12Kmusldist/bin/rustc

AhoraposeesunRustconmuslhabilitado!Ydebidoaquelohemosinstaladoenunprefijopersonalizadonecesitamosasegurarnosdequenuestrosistemapuedaencontrarlosejecutablesybibliotecasapropiadascuandotratemosdeejecutarlo:

$exportPATH=$PREFIX/bin:$PATH

$exportLD_LIBRARY_PATH=$PREFIX/lib:$LD_LIBRARY_PATH

Probémoslo!

ElLenguajedeProgramacionRust

325EnlaceAvanzado

$echo'fnmain(){println!("Hola!");panic!("falla");}'>example.rs

$rustc--target=x86_64-unknown-linux-muslexample.rs

$lddexample

notadynamicexecutable

$./example

Hola!

thread'

'panickedat'failed',example.rs:1

Exito!EstebinariopuedesercopiadoacasicualquiermaquinaLinuxconlamismaarquitecturayserejecutadosinningúnproblema.

cargobuildtambiénpermitelaopción--targetpormediodelacualpuedesconstruirtuscratesnormalmente.Sinembargo,podríasnecesitarrecompilartusbibliotecasnativasconmuslantesdepoderenlazarconellas.

ElLenguajedeProgramacionRust

326EnlaceAvanzado

%PruebasdePuntosdeReferencia

ElLenguajedeProgramacionRust

327PruebasdeRendimiento

%SintaxisdeCajasyPatrones

ElLenguajedeProgramacionRust

328SintaxisBoxyPatones

%PatronesdeSlice

ElLenguajedeProgramacionRust

329PatronesSlice

%ConstantesAsociadas

Conelfeatureassociated_consts,puedesdefinirconstantescomo:

#![feature(associated_consts)]

traitFoo{

constID:i32;

}

implFoofori32{

constID:i32=1;

}

fnmain(){

assert_eq!(1,i32::ID);

}

CualquierimplementadordeFootendráquedefinirID.Sinladefinición:

#![feature(associated_consts)]

traitFoo{

constID:i32;

}

implFoofori32{

}

resultaen

error:notalltraititemsimplemented,missing:`ID`[E0046]

implFoofori32{

}

Unvalorpordefectopuedetambiénserimplementado:

ElLenguajedeProgramacionRust

330ConstantesAsociadas

#![feature(associated_consts)]

traitFoo{

constID:i32=1;

}

implFoofori32{

}

implFoofori64{

constID:i32=5;

}

fnmain(){

assert_eq!(1,i32::ID);

assert_eq!(5,i64::ID);

}

Comopuedesver,alimplementarFoo,puedesdejarlosinimplementación,comoenelcasodei32.Entoncesesteusaraelvalorpordefecto.Pero,tambiényaligualquecomoeni64,podemosagregarnuestrapropiadefinición.

Lasconstantesasociadasnonecesitansolofuncionancontraits.Unbloqueimplparaunastructounenumestambiénvalido:

#![feature(associated_consts)]

structFoo;

implFoo{

constFOO:u32=3;

}

ElLenguajedeProgramacionRust

331ConstantesAsociadas

%InvestigacionAcademica

ElLenguajedeProgramacionRust

332InvestigaciónAcadémica

top related