rdzawe łańcuchy(1): z węża do wielbłąda

Hej! Będzie to seria wpisów (a w zasadzie już jest to seria wpisów, bo już to zacząłem pisać). W każdym razie będę tu omawiał różne ciekawostki jeśli chodzi o stringi w Ruście. Np. dzisiaj omówimy sobie, w jaki sposób można napisać funkcję, która konwertuje takiego stringa

"foo_bar_baz"

na takiego:

"fooBarBaz"

Czyli zamieniamy z notacji snake case na camel case. Taka zabawa. A więc zaczynamy.

napiszemy sobie funkcję main oraz sygnaturę naszej funkcji:

fn main() {
    println!("{}", to_camel_case("foo_bar_baz_qwerty"));
}

fn to_camel_case(s: &str) -> String {

}

Jak widać jest to funkcja, która pobiera wycinek stringa (string slice) i zwraca stringa o typie String (chodzi o to, że potrzebujemy stworzyć nowego stringa i go zaalokować na starcie, dlatego zwracamy String, a nie &str).

Dalej, stworzymy również wspomnianego już Stringa i go zwrócimy (na razie jest pusty). Możemy to zrobić poprzez `return out;`. Ale powiedzmy, że jesteśmy leniwi i nie chce nam się pisać średnika na końcu. Jeśli tak zrobimy, to i return nie będziemy musieli pisać, bo w Rust return jest domyślny na końcu funkcji jak nie dasz średnika. Więc zostanie samo `out`:

fn to_camel_case(s: &str) -> String {
    let mut out = String::new();
    out 
}

Zauważ, deklarujemy go z modyfikatorem `mut`, ponieważ będziemy go mutować poprzez dodawanie do niego kolejnych stringów

Potrzebujemy jakoś oddzielić wyrazy przedzielone znakiem podkreślenia, do tego posłuży nam metoda split:

s.split("_")

wykorzystamy ją w pętli:

for word in s.split("_") {
        out += &word[..1].to_uppercase();
        out += &word[1..];
}

&word[..1] zwraca nam wycinek stringa zawierający pierwszą literę, zamieniamy go na uppercase i dodajemy do stringa `out`

&word[1..] zwraca nam wycinek stringa zawierający wszystko poza pierwszą literą i to dodajemy niezmienione.

Jaki jest tego efekt?

Wywołanie to_camel_case("foo_bar_baz_qwerty") zwraca "FooBarBazQwerty"

No dobra, ale co jeśli chcemy, żeby pierwsza litera była mała (fooBarBazQwerty)?

Możemy sprawdzić w pętli, czy jest to pierwsza iteracja i jeśli tak, to dodać słowo w niezmienionej formie. Żeby to osiągnąć, trochę zmienimy naszą pętlę.

Zamiast for word in s.split("_") napiszemy for (i, word) in s.split("_").enumerate(), żeby mieć dostęp do zmiennej oznaczającej numer iteracji. Tradycyjnie nazywamy tę zmienną `i`

W samym ciele pętli natomiast dodajemy warunek sprawdzający, czy jest to pierwsza(tj. zerowa) iteracja i jeśli tak, to dodajemy słowo niezmienione:


        if i == 0 {
             out += word;
        } else {
             out += &word[..1].to_uppercase();
             out += &word[1..];
        }

Całość programu będzie wyglądać tak:

fn main() {
    println!("{}", to_camel_case("foo_bar_baz_qwerty"));
}

fn to_camel_case(s: &str) -> String {
    let mut out = String::new();
    for (i, word) in s.split("_").enumerate() {
        if i == 0 {
            out += word;
        } else {
            out += &word[..1].to_uppercase();
            out += &word[1..];
        }
    }
    out
}

A jak odpalimy to przez carego run, to nam się wyświetli:

fooBarBazQwerty

I tu jest dobrze, jednak jak dodamy nieco inny string wejściowy, taki kończący się znakiem podkreślenia:

println!("{}", to_camel_case("foo_bar_"));

To program panikuje! Problem jest w tym, że wtedy word na końcu będzie pustym stringiem, więc tego typu wyrażenie &word[..1] spowoduje panikę, bo próbujemy się dostać do elementu 1, jak nie ma takiego itemu. Dlatego możemy dodać dodatkowy warunek i sprawdzać, czy string nie jest pusty:

} else if word.len() > 0 {

Tak więc cały program po poprawkach wyglądać będzie tak:

fn main() {
    println!("{}", to_camel_case("foo_bar_baz_qwerty"));
    println!("{}", to_camel_case("foo_bar_"));
    println!("{}", to_camel_case("_foo_bar"));
}

fn to_camel_case(s: &str) -> String {
    let mut out = String::new();
    for (i, word) in s.split("_").enumerate() {
        if i == 0 {
            out += word;
        } else if word.len() > 0 {
            out += &word[..1].to_uppercase();
            out += &word[1..];
        }
    }
    out
}	

⭐️ W zasadzie tu jest jeszcze jedna poprawka do zrobienia, ale to już zostawiam tobie. Mianowicie jak dasz podkreślnik na początku:

println!("{}", to_camel_case("_foo_bar"));

To wyświetli FooBar (zamiast fooBar). Chodzi o to, że wtedy pusty string będzie pierwszym słowem, a foo będzie drugim, więc będzie uppercase'owane. No ale jak powiedziałem - to już wyzwanie dla ciebie. Zadanie z gwiazdką.


No, to był mój pierwszy tutorial do Rusta, jaki zrobiłem. Jak ci się podobało? Bo mi ciekawie się to pisało i chcę więcej takich pisać. Więc oczekuj kolejnych podobnych wpisów.

Komentarze

Popularne posty z tego bloga

Dlaczego nie da się nadgonić frontendu

Absurdy Rekrutacji 2023

Przygody juniora (1)