Een van de eerste hulpmiddelen die je zult tegenkomen wanneer je met Rust begint, is Cargo. Dit is niet alleen de bouwtool voor Rust, maar ook de pakketbeheerder en de testloper. Cargo is essentieel voor elke project in Rust. Elke les zal je aansteken om een nieuw project te maken met Cargo, en ik raad aan om dit in je oplossingsmap te doen. Je kopieert de testmappen van het boekrepository in je projectmap, zodat je de code kunt testen. Als je nieuwsgierig bent naar hoe testen werken met Cargo en Rust, kun je de tests voor hoofdstuk 1 uitvoeren. Verander naar de map 01_hello van het boek en voer de tests uit met de volgende opdracht:

bash
$ cd command-line-rust/01_hello
$ cargo test

Als alles goed gaat, zou je een bericht moeten zien waarin staat dat alle tests geslaagd zijn, bijvoorbeeld:

bash
running 3 tests test false_not_ok ... ok test true_ok ... ok test runs ... ok

De programma’s die in dit boek staan, zijn getest op macOS, Linux, Windows 10/PowerShell, en Ubuntu Linux/Windows Subsystem for Linux (WSL). Hoewel Rust uitstekend werkt op zowel Windows als Unix-achtige besturingssystemen, werken sommige programma’s zoals findr en lsr iets anders op Windows vanwege de fundamentele verschillen tussen Unix-achtige systemen en Windows. Voor Windows/PowerShell-gebruikers wordt aangeraden om WSL te installeren en door de programma’s in die omgeving te werken.

Alle code in dit boek is geformatteerd met rustfmt, een handige tool om je code netjes en leesbaar te maken. Je kunt cargo fmt gebruiken om rustfmt uit te voeren op alle bronbestanden van een project, of je kunt het integreren in je teksteditor zodat het automatisch draait wanneer je je werk opslaat. Bijvoorbeeld, ik gebruik de teksteditor Vim, die automatisch rustfmt uitvoert elke keer wanneer ik mijn werk opsla. Dit maakt het veel gemakkelijker om mijn code te lezen en eventuele fouten te vinden.

Daarnaast raad ik aan om Clippy te gebruiken, een linter voor Rust-code. Linten betekent het automatisch controleren van code op veelvoorkomende fouten, en de meeste programmeertalen bieden een of meer linters aan. Zowel rustfmt als Clippy zouden standaard geïnstalleerd moeten zijn, maar als je Clippy nog niet hebt, kun je het toevoegen met de volgende opdracht:

bash
rustup component add clippy

Vervolgens kun je de volgende opdracht uitvoeren om Clippy je broncode te laten controleren en aanbevelingen te doen:

bash
cargo clippy

Als er geen uitvoer van Clippy komt, betekent dit dat er geen suggesties zijn.

In maart 2024 werd dit boek geüpdatet. De taal Rust en de crates zijn in de afgelopen twee jaar snel geëvolueerd, en ik ben dankbaar dat O'Reilly me de mogelijkheid heeft gegeven om mijn codevoorbeelden aan te passen om deze veranderingen te weerspiegelen. Ik heb de programma's vereenvoudigd zodat ze gemakkelijker te onderwijzen zijn en de testuitvoer verbeterd door de pretty_assertions crate te gebruiken. De grootste verandering was de clap crate (de command-line argument parser) die in elk programma vanaf hoofdstuk 2 wordt gebruikt. Toen ik het boek schreef, was de clap crate op versie 2.33 en had het slechts één methode, de builder-methode. De versie steeg snel naar 4 en introduceerde een tweede methode, derive. Ik herschreef alle mijn programma’s om de nieuwe patronen te gebruiken, waarbij ik ervoor zorgde dat de lezer gemakkelijk andere code kan gebruiken.

De codevoorbeelden in de geüpdatete versie van dit boek zijn te vinden door de branches clap_v4 en clap_v4_derive te checken voor respectievelijk de builder- en derive-patronen.

De conventies die in dit boek worden gebruikt zijn ook belangrijk om te begrijpen. Italics worden gebruikt om nieuwe termen, URL's, e-mailadressen, bestandsnamen en bestandsextensies aan te geven. Constant width wordt gebruikt voor programmalijsten en om specifieke elementen zoals variabele- of functienamen te beschrijven. Daarnaast wordt constant width bold in blokken code gebruikt om speciale aandacht te geven aan bepaalde onderdelen die worden besproken, en constant width italic toont tekst die door de gebruiker moet worden vervangen.

Het boek bevat ook aanvullende materialen zoals codevoorbeelden en oefeningen die je kunt downloaden van https://oreil.ly/commandlinerust_code. Als je technische vragen hebt over het gebruik van de codevoorbeelden, kun je e-mailen naar [email protected]. In het algemeen kun je de voorbeeldcode uit dit boek gebruiken in je eigen programma's en documentatie, maar het is belangrijk te weten dat het verkopen of verspreiden van voorbeeldcode zonder toestemming niet is toegestaan.

Een ander belangrijk aspect is het feit dat je vaak geen toestemming hoeft te vragen voor het opnemen van kleine delen code, bijvoorbeeld wanneer je de code gebruikt in een programma dat verschillende fragmenten van dit boek combineert. Echter, wanneer je grote hoeveelheden code uit dit boek gebruikt in je productdocumentatie, is toestemming wel vereist. Als je twijfelt of je gebruik van de code voorbeelden buiten de fair use valt, kun je contact opnemen met O’Reilly via [email protected].

Een belangrijk onderdeel van het leren van Rust is ook het begrijpen van hoe de taal evolueert en zich aanpast aan nieuwe versies. Het is goed om te weten dat deze veranderingen kunnen optreden, en dat je niet bang hoeft te zijn om met verouderde tools of versies te werken zolang je je bewust bent van de updates.

Hoe werkt de cut-opdracht en wat zijn de mogelijkheden bij het verwerken van tekstbestanden?

De cut-opdracht is een veelgebruikte tool in Unix-achtige systemen die wordt gebruikt om geselecteerde delen van tekstregels uit bestanden te extraheren en weer te geven. De kracht van deze opdracht ligt in de eenvoud en de flexibiliteit waarmee je bepaalde kolommen of velden kunt isoleren, wat nuttig is voor gegevensanalyse, rapportage of het bewerken van gestructureerde tekstbestanden.

Met de cut-opdracht kunnen de geselecteerde delen van elke regel worden bepaald op basis van de positie van de kolom of veld, wat betekent dat je precies kunt aangeven welke informatie je wilt extraheren, afhankelijk van de indeling van het bestand. Er zijn twee primaire manieren om de cut-opdracht te gebruiken: op byte- of karakterniveau (met behulp van de -b en -c opties) of op veldniveau (met de -f optie). In dit hoofdstuk focussen we op deze twee benaderingen, en hoe ze effectief kunnen worden toegepast op verschillende bestandsindelingen.

De basisstructuur van de cut-opdracht is als volgt:

bash
cut -b lijst [-n] [bestand ...] cut -c lijst [bestand ...] cut -f lijst [-d scheidingsteken] [-s] [bestand ...]

Werken met vaste breedtekolommen

Een van de eenvoudigste toepassingen van de cut-opdracht is het extraheren van vaste kolommen uit tekstbestanden die een specifieke breedte hebben. Denk bijvoorbeeld aan een tekstbestand waarin elke kolom een vast aantal tekens inneemt. Stel je een bestand voor met de naam boeken.txt dat een lijst van boeken bevat, met de auteur, publicatiejaar en titel:

plaintext
Auteur Jaar Titel Émile Zola 1865 La Confession de Claude Samuel Beckett 1952 Waiting for Godot Jules Verne 1870 20,000 Leagues Under the Sea

In dit geval kan de cut-opdracht worden gebruikt om bijvoorbeeld alleen de naam van de auteur (die de eerste 20 tekens inneemt) te extraheren:

bash
cut -c 1-20 boeken.txt

Dit zou resulteren in de volgende uitvoer:

plaintext
Auteur Émile Zola Samuel Beckett Jules Verne

Als je vervolgens alleen het publicatiejaar wilt extraheren, kun je de volgende opdracht gebruiken:

bash
cut -c 21-25 boeken.txt

De uitvoer zou het jaar van publicatie zijn:

plaintext
Jaar 1865 1952 1870

Door het gebruik van de cut-opdracht kun je eenvoudig gegevens extraheren op basis van hun positie, zelfs als het bestand geen scheidingstekens tussen de kolommen heeft.

Werken met gescheiden velden

In de praktijk komen gescheiden tekstbestanden, zoals tab-gescheiden of komma-gescheiden bestanden, veel vaker voor dan vaste breedtebestanden. Stel dat je een bestand hebt waarin de velden worden gescheiden door tabs, zoals boeken.tsv:

plaintext
Auteur Jaar Titel
Émile Zola 1865 La Confession de Claude Samuel Beckett 1952 Waiting for Godot Jules Verne 1870 20,000 Leagues Under the Sea

In dit geval kun je de -f optie gebruiken om specifieke velden op te halen, bijvoorbeeld het jaar en de titel:

bash
cut -f 2,3 boeken.tsv

De uitvoer zou er als volgt uitzien:

plaintext
Jaar Titel
1865 La Confession de Claude 1952 Waiting for Godot 1870 20,000 Leagues Under the Sea

Een andere veelvoorkomende indeling is het komma-gescheiden bestand, oftewel een CSV-bestand. Het bestand boeken.csv kan er bijvoorbeeld als volgt uitzien:

plaintext
Auteur,Year,Title Émile Zola,1865,La Confession de Claude Samuel Beckett,1952,Waiting for Godot Jules Verne,1870,20,000 Leagues Under the Sea

In dit geval moet je de -d optie gebruiken om de komma als scheidingsteken aan te geven:

bash
cut -d , -f 2,3 boeken.csv

De uitvoer zou dan zijn:

plaintext
Year,Title 1865,La Confession de Claude 1952,Waiting for Godot 1870,20,000 Leagues Under the Sea

Bij CSV-bestanden moet je echter voorzichtig zijn met velden die de scheidingsteken bevatten. Bijvoorbeeld, de titel "20,000 Leagues Under the Sea" bevat een komma, en daarom is het veld tussen aanhalingstekens geplaatst. Het is belangrijk op te merken dat de cut-opdracht, zelfs met de -d optie, deze komma in het veld niet kan negeren, wat kan leiden tot problemen bij het verwerken van dergelijke gegevens.

Fouten en waarschuwingen

Bij het gebruik van de cut-opdracht kunnen fouten optreden, zoals het selecteren van niet-bestaande velden of het geven van ongeldige opties. In dergelijke gevallen zal de opdracht een foutmelding weergeven. Bijvoorbeeld:

bash
cut -f foo,bar boeken.tsv
cut: [-cf] lijst: ongeldige lijstwaarde

Als een bestand niet gevonden kan worden, wordt dit ook gemeld:

bash
cut -c 1 boeken.txt blargh cut: blargh: Geen dergelijk bestand of map

De cut-opdracht zal standaard vanuit de standaardinvoer lezen als geen bestand is opgegeven of als het bestand als een koppelteken ('-') wordt aangegeven. Dit maakt het mogelijk om de opdracht in een pijplijn te gebruiken, bijvoorbeeld:

bash
cat boeken.tsv | cut -f 2

Wat nog belangrijk is om te begrijpen

Het is cruciaal te begrijpen dat de cut-opdracht niet in staat is om de volgorde van de velden te wijzigen. Als je probeert om velden in een andere volgorde weer te geven, bijvoorbeeld door eerst het tweede veld en daarna het eerste veld te selecteren, zal de uitvoer de oorspronkelijke volgorde behouden. Dit is een beperking van de tool, die bedoeld is voor het extraheren van gegevens zonder de interne structuur van de gegevens te veranderen.

Daarnaast moeten we de verschillen tussen het werken met bytes en tekens begrijpen. Bij het werken met vaste breedtebestanden, kan het gebruik van bytes problematisch zijn bij bepaalde tekens, zoals Unicode-tekens die meer dan één byte kunnen innemen. Hierdoor kan de uitvoer ongeldig worden, zoals te zien was bij het gebruik van de -b optie met een bestand dat een Unicode-teken bevatte.

De cut-opdracht is een krachtige tool voor het manipuleren van tekstbestanden, maar er zijn beperkingen bij het werken met geavanceerdere bestandsindelingen, zoals CSV-bestanden met escapen van delimiters. Het is belangrijk te weten wanneer je cut moet gebruiken en wanneer je beter kunt overschakelen naar een krachtigere tool die met complexe bestandsindelingen kan omgaan.

Hoe werken argumenten en het parseren van tekstbestanden in Rust?

In Rust, een taal die vooral bekend staat om zijn snelheid en veiligheid, komt het werken met commandoregelargumenten en tekstbestanden vaak voor. Dit kan variëren van het extraheren van karakters of bytes uit tekstbestanden tot het valideren van delimiters en het correct verwerken van argumenten. De juiste aanpak voor het verwerken van deze gegevens kan een wezenlijk verschil maken in de efficiëntie van een applicatie.

Wanneer een programma commando-argumenten ontvangt, is het belangrijk om goed gedefinieerde argumenten te creëren die een consistente en betrouwbare uitvoer opleveren. Een typisch voorbeeld van het werken met commandoregelargumenten is het gebruik van de clap crate, die helpt bij het eenvoudig definiëren van de verwachte parameters en het valideren van de ingediende waarden. Het gaat daarbij niet alleen om het herkennen van opties en parameters, maar ook om het correct interpreteren van wat elke argument doet.

Het extraheren van karakters of bytes is een fundamenteel proces in veel applicaties. Soms is het nodig om specifieke secties van een tekstbestand te isoleren of bepaalde bytes te extraheren om vervolgens te verwerken. Dit kan bijvoorbeeld handig zijn bij het lezen van gestructureerde gegevens zoals CSV-bestanden, waar elke waarde een apart veld is gescheiden door een delimiter. In zo'n geval moeten we zorgen dat de delimiter goed wordt gevalideerd, zodat we correct kunnen interpreteren waar het ene veld eindigt en het volgende begint.

Een belangrijk aspect van het parseren van tekstbestanden is de keuze van een geschikte delimiter. De delimiter is essentieel voor het correct splitsen van de tekst in verwerkbare eenheden. Veelgebruikte delimiters zijn bijvoorbeeld tabs of komma’s, maar het is cruciaal dat de juiste delimiter wordt gekozen en gevalideerd om fouten in de verwerking te voorkomen. In de praktijk moeten we een mechanisme inbouwen dat controleert of de juiste delimiter is ingesteld en of deze consistent door het bestand heen wordt gebruikt.

Wanneer we het hebben over het selecteren van bepaalde velden uit een csv::StringRecord (de gegevensstructuur die de individuele regels van een CSV-bestand vertegenwoordigt), moeten we ervoor zorgen dat de juiste index wordt gebruikt om de gewenste velden te extraheren. Hierbij is het belangrijk om te begrijpen hoe de indexering werkt en wat de implicaties zijn van nul-indexering in de context van Rust.

De cyclomatische complexiteit speelt een cruciale rol in het testen van een programma. Deze maat voor de complexiteit van een programma kan ons helpen begrijpen hoe makkelijk of moeilijk het is om een programma te testen en de mogelijke paden die de uitvoering kan volgen. Het verhogen van de cyclomatische complexiteit betekent vaak dat er meer gevallen zijn om te testen en dat de code moeilijker te begrijpen is, wat kan leiden tot onvoorziene bugs.

Bij het werken met tijdstempels en datums is het essentieel om structuren zoals DateTime of Datelike correct te definiëren en te valideren. De functies die worden geleverd door de chrono crate kunnen helpen bij het manipuleren van datums, zoals het verkrijgen van de maand, het jaar of de dag van de maand. De juiste validatie van datums is cruciaal om fouten bij de tijdsverwerking te voorkomen, vooral wanneer we werken met tijdgevoelige gegevens of logbestanden.

Bij het implementeren van functies zoals grep of cut is het belangrijk om te begrijpen hoe de programma’s omgaan met escape-sequenties en speciale tekens. Dit kan invloed hebben op de manier waarop we tekst manipuleren en interpreteren. Het gebruik van escape-sequenties in de Style::reverse functie kan bijvoorbeeld de visuele weergave van de uitvoer beïnvloeden, en moet dus correct worden behandeld om verwarring te voorkomen.

Het werken met bestanden en de toegang tot verschillende directories en bestanden vereist een gedegen begrip van besturingssysteemfunctionaliteit. Functies zoals metadata::is_dir en DirEntry::file_name stellen ons in staat om informatie over bestanden en directories op te vragen, maar we moeten ook rekening houden met onleesbare directories en bestandspaden die niet bestaan. Het correct afhandelen van deze gevallen voorkomt dat ons programma crasht of foutieve informatie retourneert.

Het testen van programma’s is niet alleen een kwestie van het controleren van de uitvoer tegen een verwachte waarde. Het is ook belangrijk om integratietests te schrijven die ervoor zorgen dat de verschillende componenten van het programma correct samenwerken. Dit kan bijvoorbeeld betekenen dat we ervoor zorgen dat de invoer correct wordt gelezen, dat de argumenten goed worden verwerkt en dat de juiste uitvoer wordt gegenereerd.

De manier waarop een programma omgaat met foutmeldingen is net zo belangrijk als de manier waarop het de reguliere uitvoer behandelt. Fouten moeten duidelijk en informatief zijn, zodat gebruikers en ontwikkelaars precies begrijpen waar het probleem ligt. Dit betekent dat foutmeldingen niet alleen moeten beschrijven wat er mis is, maar ook waarom het misgaat, en zo nodig, hoe het kan worden opgelost.

Naast het hierboven beschreven proces van het werken met argumenten en tekstverwerking, is het van belang om altijd te zorgen voor een goed beheer van afhankelijkheden. Het gebruik van de clap crate voor argumentparsing en andere bibliotheken kan helpen om het ontwikkelingsproces te versnellen, maar het is essentieel om ervoor te zorgen dat de versies van deze afhankelijkheden compatibel zijn met de rest van de projectconfiguratie.

Het goed definiëren en valideren van de argumenten, samen met het zorgvuldig parseren van tekstbestanden, zijn de sleutel tot het bouwen van robuuste en efficiënte programma's in Rust. Deze benaderingen stellen ontwikkelaars in staat om betrouwbare applicaties te bouwen die bestand zijn tegen verschillende invoerscenario’s en altijd de verwachte uitvoer leveren.