#Klooienmetcomputers

Worstelen met traits

Arnout van Kempen over rommelen in een digitale wereld.

Traits zijn een concept van Rust dat de leesbaarheid van programma's ten goede komt en handig bovendien. Maar het is ook wel behoorlijk worstelen om ze goed te begrijpen vind ik. Vandaar de titel.

We zijn traits al eerder tegengekomen in een heel specifieke vorm: in de vorige aflevering definieerden we een functie, die alleen kan werken op een vector van ordinale elementen. Daartoe gaven we een generiek type aan, met de beperking dat de trait PartialOrd voor het gebruikte type gedefinieerd moet zijn. Rust definieert zelf talloze traits, dus het is handig die te kunnen gebruiken.

Maar vandaag gaan we zelf een paar traits maken, in een functie aanroepen, en de uitkomsten op het scherm plaatsen. Maar eerst de vraag wat een trait eigenlijk is.

Enkele weken geleden zagen we hoe we een methode konden maken voor een type. We maakten een struct, en met impl voegden we daar een functie aan toe. Maar wat nu als we dezelfde methode op verschillende typen willen toepassen, allemaal met dezelfde naam? Dat kan in allerlei omstandigheden handig zijn, maar hier gebruik ik een betrekkelijk nutteloos voorbeeld dat wel inzichtelijk is. Stel, we hebben een vierkant, een cirkel en een driehoek. Drie geometrische figuren. Voor alle drie deze figuren kan je op basis van een beperkt aantal argumenten de oppervlakte berekenen, maar de manier waarop je dat doet, verschilt per figuur. De oppervlakte van een vierkant is het kwadraat van een zijde, bij een cirkel is dat het kwadraat van de straal maal pi en bij een driehoek is het de helft van de basis maal de hoogte.

Een trait is nu de generieke aanduiding van een functie die de oppervlakte berekent. Per figuur moet je vervolgens de methode uitwerken. Dat ziet er bijvoorbeeld als volgt uit:

trait OppervlakteBerekening {
    fn oppervlakte(&self) -> f64;
}

Hiermee weet je dat er een trait bestaat, dat die een functie implementeert zonder argumenten en met een float als returnwaarde. Vervolgens maak je voor een vierkant: 

struct Vierkant {
    zijde: f64,
}

impl OppervlakteBerekening for Vierkant {
    fn oppervlakte(&self) -> f64 {
        self.zijde * self.zijde
    }
}

In je main-functie kan je nu bijvoorbeeld zeggen:

let vierkant = Vierkant { zijde: 5.0 };
let oppervl = vierkant.oppervlakte(); 

Op dezelfde manier kan je nu een oppervlakte-methode voor andere figuren definiëren en gebruiken.
Wat ook kan is een trait maken met een generieke methode, die dus op alle structs waar deze wordt geïmplementeerd precies hetzelfde werkt. Neem de (nogal nutteloze) methode kleur die ongeacht het figuur aangeeft dat dit figuur groen is:

trait Kleur {
    fn kleur(&self) -> String {
        String::from(“groen”)
    }
}

Dit keer is de functie al direct in de trait beschreven. Een implementatie voor een specifiek figuur kan afwijken, maar je kan ook van de standaardfunctie gebruik maken met:

impl Kleur for Vierkant {}

En nu zal vierkant.kleur() als returnwaarde “groen” geven. 

Ten slotte nog een aardigheidje: Je kan ook functies maken die op een trait werken. Aangezien de uitkomst van de trait altijd hetzelfde is, in ons geval een f64, kan je daarmee een universele functie maken voor alle onderliggende types. Stel bijvoorbeeld dat je de inhoud wilt weten van een blokje dat je zaagt uit een plank met een gegeven dikte, in een van de verschillende vormen. De inhoud zal dan altijd gelijk zijn aan de dikte van de plank maal het oppervlak van de vorm. Dat los je dan op met 

fn inhoud(figuur: &impl OppervlakteBerekening, hoogte: f64) -> f64 {
    figuur.oppervlakte() * hoogte
}

Zoals altijd op GitHub een voorbeeld van werkende code waarin deze principes worden uitgewerkt.

Wie mee wil doen met #klooienmetcomputers kan dat doen via GitHub. Maak een account op github.com en zoek naar Abmvk/kmc. Het account Abmvk volgen kan ook. Lezers zijn vrij te gebruiken wat ze willen en om zelf zaken toe te voegen of aan te passen, vragen te stellen of commentaar te leveren.

Arnout van Kempen di CCO CISA is Senior manager Risk & Compliance bij Baker Tilly. Hij schrijft op persoonlijke titel. Hij is lid van de Commissie Financiƫle verslaggeving & Accountancy van de AFM en lid van de signaleringsraad van de NBA. Daarnaast is hij diaken van het bisdom 's-Hertogenbosch.

Gerelateerd

reacties

Reageer op dit artikel

Spelregels debat

    Aanmelden nieuwsbrief

    Ontvang elke werkdag (maandag t/m vrijdag) de laatste nieuwsberichten, opinies en artikelen in uw mailbox.

    Bent u NBA-lid? Dan kunt u zich ook aanmelden via uw ledenprofiel op MijnNBA.nl.