Laboratoria 11 XII

Celem zajęć jest zaznajomienie się z pisaniem makr w języku Rust.

Makra deklaratywne

Napisz makro do definiowania grafu korzystając z konstrukcji macro_rules! .

Do reprezentacji grafów używać będziemy:

struct Graph {
  name: &'static str,
  nodes: Vec<Node>,
}

struct Node {
  neighbors: Vec<usize>,
}

Przykładowe użycia makra:

// Poniższe wywołanie powinno zbudować graf reprezentujący nieskierowaną krawędź.
graph!(
  Edge, // nazwa grafu
  undirected, // krawędzie są nieskierowane
  2, // ilość wierzchołków
  (1, 2) // lista krawędzi
);

// Poniższe wywołanie powinno zbudować skierowany cykl na 4 wierzchołkach.
graph!(
  Cycle,
  directed,
  4,
  (1, 2), (2, 3), (3, 4), (4, 1)
);

Dla upewnienia, dopisz kilka testów jednostkowych. Dla przykładu:

assert_eq!(graph!(Empty, directed, 0), Graph { name: "Empty", nodes: vec![] });

Materiały:

Makra proceduralne

Dany jest trait:

trait MonoidElement: Add + AddAssign {
  fn zero() -> Self;
}

Napisz makro proceduralne, które pozwoli automatycznie derywować MonoidElement dla dowolnego typu, który jedno ze swoich pól ma oznaczone atrybutem #[monoid_element]. Dla przykładu, w sytuacji:

#[derive(MonoidElement)]
struct ComplexStruct {
  a: (f32, f32),
  b: [u8; 4],
  #[monoid_element]
  c: u32,
}

ComplexStruct powinno implementować interfejs elementu monoidu tylko ze względu na pole c. Innymi słowy, zero powinno zwrócić element neutralny (domyślny) dla u32, a metody add oraz add_assign powinny modyfikować / brać pod uwagę wyłącznie pole c.

Materiały