Een Minecraft Discord Bot in Rust - Deel 1 - Een Nieuwe Programmeertaal Leren met ChatGPT

Rust tools voor een Discord Bot en ChatGPT trucjes.

Rust
Discord Bot
ChatGPT
Author

Mees Molenaar

Published

November 29, 2023

Inleiding

Al meerdere keren dit jaar heb ik gehoord dat Rust een interessante programmeertaal is. Dat begon bij het advies van een oud collega, tot het horen van Polars bij Xomnia en verschillende video’s van ThePrimeagen . Daardoor begon het bij mij te kriebelen en wilde ik een klein project beginnen om kennis te maken met Rust.

Waarom Een Minecraft Discord Bot

Op zoek naar kleine beginnersprojecten vond ik dat het maken van een Discord bot een goed idee kan zijn. Daarnaast heeft een vriend van mij een YouTube kanaal MeneerKB met een Discord server voor zijn community. Hij maakt Minecraft video’s en op zijn server heb ik advies gevraagd naar een idee voor wat handig is om met de Minecraft Discord bot te gaan doen. Zij kwamen met het advies om een bot te maken die je naar het recept van een item kunt vragen en die een overzichtelijk tabel met de ingrediënten terug geeft. Voordat we daaraan beginnen (dat zal in de volgende post zijn), wil ik eerst nog terugkomen waarom Rust een interessante taal is en hoe je ChatGPT kunt gebruiken om een nieuwe taal te leren.

Waarom Rust

Zoals iedere programmeertaal heeft Rust zijn voor- en nadelen. Dit is mijn persoonlijke lijst met belangrijke voor- en nadelen, maar wees ervan bewust dat er vast nog andere voor- en nadelen zijn.

Voordelen van Rust

Rust is snel

Rust is een programmeertaal die dichter bij de machine taal (de taal die computer hardware spreekt) ligt dan bijvoorbeeld Python.

Dit wordt vaak een “lagere” taal genoemd, ten opzichte van Python wat dan een “hogere” taal is.

Hierdoor kan je je code specifiek voor stukken hardware maken zonder dat er functionaliteiten zijn ingebouwd die je niet nodig hebt. Omdat deze functionaliteiten niet zijn ingebouwd moet je meer zelf doen, maar daardoor mis je onnodige functionaliteiten waardoor je programma sneller is. Hierdoor kan je bijvoorbeeld efficiënter het geheugen beheren dat je programma gebruikt. Maar wees je er bewust van dat het daardoor ook makkelijker is om een bug in je programma te introduceren. Het mooie aan Rust is dat ze daar een oplossing voor hebben gevonden.

Rust heeft de Borrow Checker

De Borrow Checker is een tool die onderdeel is van de Rust compiler. Deze tool doet tijdens het compilen een controle van je code, waarin het controleert of je het geheugen van je programma correct beheert. Hiermee zorgt Rust ervoor dat je zelf het geheugen kunt beheren, en dus snellere programma’s maakt, terwijl je van fouten wordt behoed en geen onveilige situaties creëert zoals een Buffer Overflow.

Nadelen van Rust

De uitdrukking, ieder voordeel heeft zijn nadeel, heeft ook zijn betrekking op Rust en eigenlijk alle programmeertalen (daarom zullen er vast honderden programmeertalen zijn), maar dit zijn de door mij gekozen belangrijkste nadelen.

Rust is moeilijk

Omdat Rust een “lagere” taal is, en je dus specifieke instructies kunt geven ten opzichte van bijvoorbeeld Python, is het ingewikkelder. Je moet namelijk zelf meer en beter begrijpen wat er met je code gebeurt, omdat er maar weinig vangnetten zijn ingebouwd. En zelfs met de Borrow Checker als vangnet is het nog lastig. Bijvoorbeeld: met het beheren van geheugen komen er (nieuwe) termen zoals pointers, heap en stack om de hoek kijken. Zeker als je vooral bekend bent met “lagere” programmeertalen zoals Python, hoef je je over die termen normaliter geen zorgen te maken, want dat doet Python voor je.

Rust is minder geschikt voor prototypes

Vaak is het belangrijk om snel te bouwen, omdat je dan snel feedback kunt krijgen op je product. Daarom wordt er vaak gezegd om een Minimal Viable Product (MVP) te bouwen. Maar omdat Rust een “lagere” taal is, komt er meer bij kijken bij het maken van een product dan een “hogere” taal. Gelukkig zijn er al aardig wat Crates (equivalent van packages) die je veel werk uit handen nemen, maar ik denk nog steeds dat je langer bezig zult zijn met Rust dan bijvoorbeeld Python.

Mijn advies hier: bouw een prototype in je favoriete taal. En merk je dat je meer efficiëntie nodig hebt, schrijf die delen in Rust.

Rust is nieuw

Hoewel Rust er alweer een tijdje is (8 jaar sinds de release) is het nieuwer ten opzichte van Python en C++. Hierdoor zijn er minder libraries (Crates) en sommige features zijn nog niet ontwikkeld (misschien zijn die ook helemaal niet nodig, tot dusver miste ik nog niets). Daarnaast zijn er door de moeilijkheid en nieuwigheid ook minder mensen die Rust kunnen. Daarom denk ik dat Rust voor grotere organisaties (die al problemen hebben met het werven van goede developers) nog geen goede keuze is.

Persoonlijke Redenen Om In Rust Te Programmeren

Bovenstaande voordelen en nadelen gelden in het algemeen, maar uiteindelijk draait het erom wat je zelf belangrijk vindt. In mijn geval wil ik juist die moeilijke termen zoals pointers, de heap en de stack leren. En ik denk dat in de wereld van Big Data, efficiëntie zeer belangrijk is. Niet alleen omdat het dan sneller is, maar ook omdat het dan minder kost en minder computerkracht en elektriciteit verbruikt. En deze zaken wil ik dan het liefste leren en toepassen in een taal die gericht is op de toekomst en ik denk dat Rust dat is.

Hoe Maak Je Een Discord Bot In Rust

De Rust Tools

Hoewel deze post niet volledig gewijd is aan het maken van een Discord bot en de volgende post in meer detail hierop in zal gaan. Wil ik jullie niet onthouden van de twee tools die ik heb gebruikt voor de bot. De twee belangrijkste Crates zijn Serenity en Shuttle .

Serenity

Ik denk dat de README.md het het korste uitlegt met deze zin

Serenity is a Rust library for the Discord API

Een beetje kort door de bocht betekent dit dat je deze package kunt gebruiken om met Discord te communiceren en dus ook met je Discord Bot. Deze package maakt het dus makkelijker om een Discord Bot te maken.

Shuttle

Met Shuttle kan je heel makkelijk Rust apps deployen (online zetten voor de wereld), maar het doet nog meer dan dat: het maakt het ook makkelijker om een applicatie lokaal te draaien en voegt ook een handige manier om met Secrets te werken toe.

ChatGPT Gebruiken om Rust te Leren

Zoals eerder vermeld: Rust is voor mij een nieuwe taal en ik weet veel eigenschappen van de taal nog niet. Maar door wat programmeer-ervaring weet ik vaak wel hoe en wat ik kan en wil bereiken met een stukje code. De antwoorden van jouw eigen hoe en wat vragen zijn perfecte startpunten om ChatGPT code te laten maken.

Ik begin vaak met ChatGPT een generieke vraag te stellen, dit doe ik zodat ChatGPT beter de context begrijpt waar de vraag over gaat.

Bijvoorbeeld:

Hi ChatGPT, I am a Rust beginner and don't know the idiomatic way to program in Rust. Can you help me write that code?

ChatGPT geeft antwoord dat het natuurlijk kan helpen, soms met wat extra informatie. Vervolgens stel ik de vraag die ik beantwoord wil hebben.

Bijvoorbeeld:

I have a dictionary called recipes, with the recipe name as key and the ingredients as values. I want an idiomatic way to retrieve the ingredients when they exist otherwise I want to show a message that the recipe does not exist.

En dit is het resultaat:

fn get_ingredients(recipes: &std::collections::HashMap<&str, Vec<&str>>, recipe_name: &str) -> Result<&Vec<&str>, &'static str> {
    match recipes.get(recipe_name) {
        Some(ingredients) => Ok(ingredients),
        None => Err("Recipe not found"),
    }
}

fn main() {
    // Example dictionary (HashMap) of recipes
    let mut recipes = std::collections::HashMap::new();
    recipes.insert("Pasta", vec!["Noodles", "Sauce", "Cheese"]);
    recipes.insert("Salad", vec!["Lettuce", "Tomato", "Cucumber"]);

    // Example usage
    match get_ingredients(&recipes, "Pasta") {
        Ok(ingredients) => {
            println!("Ingredients for Pasta: {:?}", ingredients);
        }
        Err(message) => {
            println!("Error: {}", message);
        }
    }

    // Trying to get ingredients for a non-existent recipe
    match get_ingredients(&recipes, "Pizza") {
        Ok(ingredients) => {
            println!("Ingredients for Pizza: {:?}", ingredients);
        }
        Err(message) => {
            println!("Error: {}", message);
        }
    }
}

Aan deze code van ChatGPT kun je zien dat je veel kunt leren. Zo zie je dat het programma in de main functie is geschreven. Dat i.p.v een dictionary er een HashMap is gebruikt (wat dus eigenlijk een dictionary is in Rust). En je ziet bijvoorbeeld een veel gebruikte Rust eigenschap match (voor Pattern Matching). En als laatste zie je Ok() en Err(). Allemaal Rust termen die voor mij als beginner nog onbekend zijn, maar waar ik op deze manier kennis mee leer maken.

Sommige van deze termen zal je direct begrijpen, maar andere termen niet. Gelukkig kan ChatGPT direct antwoorden geven op de termen die je niet begrijpt.

Prompt:

What does Ok() do in Rust?

Antwoord:

In Rust, `Ok()` is a variant of the `Result` type. The `Result` type is used for functions that may return an error. It has two variants: `Ok(T)` for a successful result with a value of type `T`, and `Err(E)` for an error with a value of type `E`.

Here's a brief explanation:

- `Ok(T)`: Represents the successful result of a computation, where `T` is the type of the value that the computation produces. It's a convention in Rust to use `Ok` for the "happy path" or successful outcome.
    
- `Err(E)`: Represents an error that occurred during the computation, where `E` is the type of the error. This variant is used to carry information about the error.
    

When you see `Ok(value)` in Rust, it means that the operation was successful, and `value` contains the result of that successful operation. For example:
fn divide(a: f64, b: f64) -> Result<f64, &'static str> {
    if b == 0.0 {
        // Return an error if attempting to divide by zero
        Err("Cannot divide by zero")
    } else {
        // Return the result if the division is successful
        Ok(a / b)
    }
}

fn main() {
    // Example usage
    match divide(10.0, 2.0) {
        Ok(result) => {
            println!("Result of division: {}", result);
        }
        Err(error) => {
            println!("Error: {}", error);
        }
    }
}
In this example, if the division is successful, `divide` returns `Ok(result)`, where `result` is the result of the division. If there's an error (e.g., attempting to divide by zero), it returns `Err("Cannot divide by zero")`.

The `match` statement in the `main` function is then used to pattern match on the result and handle the successful and error cases accordingly.

So, in short, `Ok()` is a way to wrap a successful result in the `Result` type.

Dit maakt ChatGPT een fantastische leerpartner (bedankt ChatGPT dus ook even voor de antwoorden nadat je klaar bent).

Er is ook nog een andere manier om ChatGPT te gebruiken. Namelijk om je code te verbeteren en uit te leggen waarom de code dan beter is. Ik begin dan met deze prompt om ChatGPT context mee te geven:

Prompt:

Hi there you are an Elite Rust programmer and I am your apprentice. I am going to show you pieces of code that I would like you to explain, improve and explain these improvements. Can you do that for me?

Om vervolgens je code erin te plakken. En voila, je persoonlijke programmeer mentor.

Als laatste nog een waarschuwing: wees voorzichtig met het gebruiken van ChatGPT. ChatGPT geeft namelijk altijd antwoorden terug. Maar helaas kloppen deze antwoorden niet altijd. Daarom is het goed om sceptisch naar de antwoorden te kijken en deze altijd te beoordelen. Want als er iets bijvoorbeeld te goed klinkt om waar te zijn, is dat waarschijnlijk ook zo. En hoewel dit minpunt vervelend is, is ChatGPT nog steeds een fantastisch hulpmiddel.

Uitdagingen In Rust

Als je niet bekend bent met de “lagere” talen (zoals ikzelf) dan is vooral het geheugen beheren lastig. Normaliter hoef je je daar ook niet druk over te maken, maar omdat je dat niet doet gebruik je veel ingebouwde programmatuur waardoor je programma ook langzamer is. Dat is anders in Rust, doordat je moet nadenken over Pointers en andere geheugenzaken. En daardoor moet je ietwat anders denken als je in Rust programmeert, wat het lastiger maakt. Maar gelukkig is er dan de Borrow Checker die je daarbij helpt. Wat Rust een goede taal maakt om je daaraan te wagen.

Conclusie

In deze post heb je hopelijk geleerd waarom Rust een interessante taal is om te leren en hoe je ChatGPT kunt gebruiken om dat te doen. Daarnaast is er (heel erg) kort uitgelegd hoe je Serenity en Shuttle kunt gebruiken om een Discord Bot te maken in Rust. In de volgende post wordt er dieper in het daadwerkelijk maken van de bot gedoken.

Voor nu, bedankt voor het lezen en fijne dag!

Mees