Rust – il linguaggio – 34

RenartPuisDet

Oggi qui: /usr/local/share/doc/rust/html/book/associated-types.html, proseguendo da qui.

Temo che sarà un post poco pimpante, mi sento anche in colpa per il copiaggio esteso 🙄

Tipi associati

Associated types are a powerful part of Rust’s type system. They’re related to the idea of a ‘type family’, in other words, grouping multiple types together. That description is a bit abstract, so let’s dive right into an example. If you want to write a Graph, you have two types to be generic over: the node type and the edge type. So you might write a trait, Graph<N, E>, that looks like this:

trait Graph {
    fn has_edge(&self, &N, &N) -> bool;
    fn edges(&self, &N) -> Vec;
    // etc
}

While this sort of works, it ends up being awkward. For example, any function that wants to take a Graph as a parameter now also needs to be generic over the Node and Edge types too:

fn distance<N, E, G: Graph<N, E>>(graph: &G, start: &N, end: &N) -> u32 { ... }

Our distance calculation works regardless of our Edge type, so the E stuff in this signature is just a distraction.

What we really want to say is that a certain Edge and Node type come together to form each kind of Graph. We can do that with associated types:

trait Graph {
    type N;
    type E;

    fn has_edge(&self, &Self::N, &Self::N) -> bool;
    fn edges(&self, &Self::N) -> Vec;
    // etc
}

Now, our clients can be abstract over a given Graph:

fn distance<G: Graph>(graph: &G, start: &G::N, end: &G::N) -> u32 { ... }

No need to deal with the Edge type here!

Let’s go over all this in more detail.

Definire tipi associati

Let’s build that Graph trait. Here’s the definition:

trait Graph {
    type N;
    type E;

    fn has_edge(&self, &Self::N, &Self::N) -> bool;
    fn edges(&self, &Self::N) -> Vec;
}

Simple enough. Associated types use the type keyword, and go inside the body of the trait, with the functions.

These type declarations can have all the same thing as functions do. For example, if we wanted our N type to implement Display, so we can print the nodes out, we could do this:

use std::fmt;

trait Graph {
    type N: fmt::Display;
    type E;

    fn has_edge(&self, &Self::N, &Self::N) -> bool;
    fn edges(&self, &Self::N) -> Vec;
}

Implementare tipi associati

Just like any trait, traits that use associated types use the impl keyword to provide implementations. Here’s a simple implementation of Graph:

struct Node;

struct Edge;

struct MyGraph;

impl Graph for MyGraph {
    type N = Node;
    type E = Edge;

    fn has_edge(&self, n1: &Node, n2: &Node) -> bool {
        true
    }

    fn edges(&self, n: &Node) -> Vec {
        Vec::new()
    }
}

This silly implementation always returns true and an empty Vec<Edge>, but it gives you an idea of how to implement this kind of thing. We first need three structs, one for the graph, one for the node, and one for the edge. If it made more sense to use a different type, that would work as well, we’re just going to use structs for all three here.

Next is the impl line, which is just like implementing any other trait.

From here, we use = to define our associated types. The name the trait uses goes on the left of the =, and the concrete type we’re implementing this for goes on the right. Finally, we use the concrete types in our function declarations.

Oggetti traits con tipi associati

There’s one more bit of syntax we should talk about: trait objects. If you try to create a trait object from an associated type, like this:

let graph = MyGraph;
let obj = Box::new(graph) as Box;

You’ll get two errors:

error: the value of the associated type `E` (from the trait `main::Graph`) must
be specified [E0191]
let obj = Box::new(graph) as Box;
          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
24:44 error: the value of the associated type `N` (from the trait
`main::Graph`) must be specified [E0191]
let obj = Box::new(graph) as Box;
          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~

We can’t create a trait object like this, because we don’t know the associated types. Instead, we can write this:

let graph = MyGraph;
let obj = Box::new(graph) as Box<Graph>;

The N=Node syntax allows us to provide a concrete type, Node, for the N type parameter. Same with E=Edge. If we didn’t provide this constraint, we couldn’t be sure which impl to match this trait object to.

😦 chissà se ci sarà un seguito sui tipi associati? per ora esattamente ome si diceva all’inizio 😦

:mrgreen:

Posta un commento o usa questo indirizzo per il trackback.

Trackback

Rispondi

Inserisci i tuoi dati qui sotto o clicca su un'icona per effettuare l'accesso:

Logo di WordPress.com

Stai commentando usando il tuo account WordPress.com. Chiudi sessione /  Modifica )

Google photo

Stai commentando usando il tuo account Google. Chiudi sessione /  Modifica )

Foto Twitter

Stai commentando usando il tuo account Twitter. Chiudi sessione /  Modifica )

Foto di Facebook

Stai commentando usando il tuo account Facebook. Chiudi sessione /  Modifica )

Connessione a %s...

Questo sito utilizza Akismet per ridurre lo spam. Scopri come vengono elaborati i dati derivati dai commenti.

%d blogger hanno fatto clic su Mi Piace per questo: