tabla de contenido · el juego de las adivinanzas la cena de los filósofos rust dentro de otros...
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<[email protected]>"]
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<[email protected]>"]
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<[email protected]>"]
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{
[email protected]=>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{
[email protected]|[email protected]=>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