När man arbetar med filinläsning i Rust måste man alltid ta hänsyn till att strängar (String) i Rust strikt måste följa UTF-8-standarden. Detta innebär att när man läser in data som bytes och försöker konvertera dessa till strängar, kan processen misslyckas om de lästa bytes inte utgör giltig UTF-8. Funktionen String::from_utf8 returnerar ett resultat som indikerar om konverteringen lyckades, medan String::from_utf8_lossy erbjuder en mer förlåtande konvertering där ogiltiga UTF-8-sekvenser ersätts med en speciell ersättningskaraktär (�).
Det är dock viktigt att vara försiktig med hur man läser in bytes från filer. Ett vanligt misstag är att först läsa hela filens innehåll som en sträng och sedan omvandla denna till en bytevektor för att plocka ut en delmängd av bytes. Denna metod är inte bara ineffektiv utan kan även krascha programmet om filen är större än tillgängligt minne. Dessutom finns risk för panik (panic) om man försöker ta ut en skiva (slice) av bytes som inte existerar, exempelvis när filen är tom och man ändå ber om en byte.
Ett säkrare och ofta mer elegant tillvägagångssätt är att använda iteratorer över filens bytes med metoden file.bytes(), kombinera detta med take(num_bytes) för att läsa exakt önskat antal bytes, och sedan samla dessa i en vektor. Typannotering är nödvändig här, eftersom kompilatorn annars inte kan avgöra storleken på den samlade typen. Det går att ange typen antingen med en explicit typannotering eller med hjälp av turbofish-syntaxen ::<>. Detta säkerställer att vi arbetar med en heapallokerad vektor med fast storlek och undviker problem med ospecificerad storlek på datastrukturen.
Vidare måste man förstå att tecknet som genereras av String::from_utf8_lossy för ogiltiga sekvenser (b'\xef\xbf\xbd') inte alltid är identiskt med utdata från andra verktyg som BSD:s head, vilket kan försvåra testning och jämförelser. En lösning är att använda samma metoder för att omvandla förväntade och faktiska utdata till lossy-strängar för att kunna jämföra dem på ett likvärdigt sätt.
När man hanterar flera filer bör man också överväga att tydligt separera filutdata, exempelvis genom att skriva ut filnamn mellan filinnehållen och lägga till en extra radbrytning mellan efterföljande filer. Här kan Iterator::enumerate användas för att hålla reda på filnummer och avgöra när sådana separatorer behövs. Om man implementerar kommandoradsverktyg likt GNU head kan man även addera funktionalitet för att hantera numeriska suffix (t.ex. kilobytes med 'K') och negativa värden för att skriva ut allt utom de sista raderna eller bytena. Det kräver i sin tur att datatyperna för antal rader och bytes ändras till signerade heltal.
Det är även möjligt att utöka funktionaliteten för att läsa tecken (chars) istället för bytes, då en sträng i Rust är en sekvens av UTF-8-kodade tecken och vissa operationer görs enklare eller mer korrekt på karaktärsnivå. Att använda String::chars() underlättar detta.
Slutligen bör man beakta olika filformat, som de som innehåller Windows-linjeavslutningar (CRLF). Testning och hantering av sådana variationer är viktigt för robusthet och kompatibilitet. Att spara testfiler med olika radslut och verifiera att programmet bevarar dessa korrekt är en viktig del av en fullständig implementation.
Att förstå dessa nyanser i filinläsning i Rust är avgörande för att undvika vanliga fallgropar, som minnesproblem, panik, och felaktig hantering av ogiltiga tecken. Att arbeta med iteratorer, korrekt typannotering, och förlåtande strängkonvertering möjliggör både säker och effektiv behandling av filinnehåll.
Hur skapar man ett Rust-program för att hantera argument och slumpmässiga val?
För att börja, skapa ett nytt projekt med kommandot cargo new fortuner, och lägg sedan till följande beroenden i din Cargo.toml:
Efter att ha skapat projektet och lagt till beroenden, kopiera testkatalogen 12_fortuner/tests från boken till ditt projekt och kör cargo test. Vid denna punkt ska alla tester misslyckas, vilket ger en grund att bygga vidare på.
Programmet ska nu definiera argumenten som det kommer att ta emot. I filen src/main.rs definierar du en struktur för att hålla dessa argument:
Här representerar sources en lista med filer eller kataloger, medan pattern är en valfri sträng som används för att filtrera fortune-citat. Flaggan insensitive anger om sökningen ska vara skiftlägesokänslig, och seed är en valfri värde av typen u64 som styr de slumpmässiga urvalen.
För att definiera dessa argument i kommandoraden använder vi clap, ett populärt Rust-bibliotek för argumenthantering. Här är en skeletonfunktion för att hämta argumenten:
När programmet startar, ska det skriva ut argumenten som användaren angett:
Med dessa funktioner på plats kan programmet skriva ut en användbar hjälptext när användaren kör det utan några argument:
Om användaren inte anger några argument, ska programmet stoppa och skriva ut ett felmeddelande:
När argumenten är korrekt tolkade, bör programmet kunna visa en struktur som den här:
Vid detta stadie ska programmet kunna hantera olika typer av argument korrekt: källfiler, mönster, skiftlägesokänslighet och ett valfritt frö (seed) för slumpmässig selection. Om användaren anger ett ogiltigt värde för ett argument, exempelvis om --seed inte kan konverteras till ett giltigt u64, ska programmet avvisa det med ett felmeddelande.
Hantering av slumpmässighet i programmet
En annan viktig aspekt är den slumpmässighet som programmet ska implementera. Eftersom datorer inte gör "verkligen" slumpmässiga val, utan istället använder pseudorandom nummergeneratorer (PRNG), är det viktigt att förstå hur detta fungerar i Rust.
En pseudorandom nummergenerator gör alltid samma val för ett givet frö (seed). Detta gör det möjligt att testa programmet på ett pålitligt sätt, då vi kan använda ett känt frö och verifiera att samma resultat upprepas varje gång.
För att implementera detta, använder programmet rand-biblioteket för att skapa en PRNG, och om ett frö anges via argumenten, används det för att styra slumpen:
Här används RegexBuilder för att skapa ett reguljärt uttryck baserat på användarens mönster, och om flaggan --insensitive är aktiverad, görs sökningen skiftlägesokänslig.
Programmet ska också kunna hantera ogiltiga reguljära uttryck, vilket gör det möjligt att avvisa felaktiga mönster och säkerställa att programmet är stabilt även vid felaktig input.
Vid detta steg är det viktigt att förstå att även om programmet genererar "slumpmässiga" val, kan dessa inte anses vara verkligen slumpmässiga om inte ett korrekt frö anges. I sådana fall kommer samma resultat att upprepas varje gång programmet körs med samma frö.

Deutsch
Francais
Nederlands
Svenska
Norsk
Dansk
Suomi
Espanol
Italiano
Portugues
Magyar
Polski
Cestina
Русский