diff --git a/exemples/opaque/Makefile b/exemples/opaque/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..398598b57c292f45e62c84d40acb926f323ec6f2
--- /dev/null
+++ b/exemples/opaque/Makefile
@@ -0,0 +1,13 @@
+CC:=gcc
+CFLAGS:=-Wall -Wextra -pedantic -g -fsanitize=address -fsanitize=leak -std=c2x
+LDFLAGS:=-fsanitize=address -fsanitize=leak
+
+main: opaque.o main.o
+	$(CC) -o $@ $^ $(LDFLAGS)
+
+opaque.o: opaque.h
+
+.PHONY: clean
+
+clean:
+	rm -f *.o main
\ No newline at end of file
diff --git a/exemples/opaque/main.c b/exemples/opaque/main.c
new file mode 100644
index 0000000000000000000000000000000000000000..14f4b6632704dcbd859ee7311d040045e24dcff8
--- /dev/null
+++ b/exemples/opaque/main.c
@@ -0,0 +1,11 @@
+#include "opaque.h"
+#include <stdio.h>
+
+int main()
+{
+    table t;
+    init(&t);
+    set(t, 10);
+    printf("%d\n", get(t));
+    destroy(&t);
+}
\ No newline at end of file
diff --git a/exemples/opaque/opaque.c b/exemples/opaque/opaque.c
new file mode 100644
index 0000000000000000000000000000000000000000..2c6f09a550c23dcf1cf9f46ec16523623a34514d
--- /dev/null
+++ b/exemples/opaque/opaque.c
@@ -0,0 +1,29 @@
+#include "opaque.h"
+#include <stdlib.h>
+
+struct _table
+{
+    int len;
+    int *data;
+};
+
+void init(table *tab)
+{
+    *tab = malloc(sizeof(struct _table));
+}
+
+void destroy(table *tab)
+{
+    free(*tab);
+    *tab = NULL;
+}
+
+int get(table tab)
+{
+    return tab->len;
+}
+
+void set(table tab, int len)
+{
+    tab->len = len;
+}
diff --git a/exemples/opaque/opaque.h b/exemples/opaque/opaque.h
new file mode 100644
index 0000000000000000000000000000000000000000..9c878701313c94ab9acd830f1eed4713f40a59e0
--- /dev/null
+++ b/exemples/opaque/opaque.h
@@ -0,0 +1,13 @@
+#ifndef OPAQUE_H
+#define OPAQUE_H
+
+struct _table;
+
+typedef struct _table *table;
+
+void init(table *tab);
+void set(table tab, int len);
+int get(table tab);
+void destroy(table *tab);
+
+#endif
diff --git a/slides/opaque.md b/slides/opaque.md
new file mode 100644
index 0000000000000000000000000000000000000000..d14b1a8beb0befadc42de36cee42627d4206628f
--- /dev/null
+++ b/slides/opaque.md
@@ -0,0 +1,153 @@
+---
+title: "Types opaques"
+date: "2023-03-07"
+---
+
+# Types composés 
+
+* Jusqu'ici les `struct` sont dans les `.h` et sont *transparents*
+
+```C 
+// table.h
+typedef struct _table {
+    int *data;
+    int length;
+} table;
+// main.c
+table tab; // membres de tab accessibles directement
+tab.length = 10;
+tab.data = malloc(tab.length * sizeof(int));
+tab.data[9] = 10;
+```
+
+# Types opaques
+
+* Afin de cacher les détails de l'implémentation.
+* Afin d'éviter les modifications directs des données.
+* Afin de protéger le monde de la dévastation!
+* Définition de types **opaques**:
+    * Variables dans les structures ne sont pas accessibles.
+    * Variables dans les structures ne sont pas modifiables.
+    * Les variables ne sont même pas connues.
+* Nécessité de passer par des fonctions pour initialiser/modifier les instances
+  de types opaques.
+* Très souvent utilisés pour les structures de données abstraites (table de
+  hachage, pile, file, ...).
+
+# Utilisation d'un type opaque: problème?
+
+* Dans `opaque.h`
+
+    ```C
+    struct table;
+    ```
+* Dans `opaque.c`
+
+    ```C
+    struct table {
+        int a;
+    }
+    ```
+* Dans `main.c`
+
+    ```C
+    int main() {
+        struct table t;
+    }
+    // error: storage size of ‘t’ isn’t known
+    ```
+* La taille de `table` n'est pas connue à la compilation!
+* Comment faire?
+
+# Utilisation d'un type opaque: pointeur!
+
+\footnotesize
+
+* Dans `opaque.h`
+
+    ```C
+    struct table;
+    struct table *create();
+    void init(struct table **t);
+    ```
+* Dans `opaque.c`
+
+    ```C
+    struct table {
+        int a;
+    }
+    struct table *create() {
+        struct table *t = malloc(sizeof(*t));
+        return t;
+    }
+    void init(struct table **t) {
+        *t = malloc(sizeof(**t));
+    }
+    ```
+* Dans `main.c`
+
+    ```C
+    int main() {
+        struct table *t = create();
+        init(&t);
+        t->a = 2; // Interdit, set(2)
+        printf("%d\n", t->a); // Interdit, get()
+    }
+    ```
+
+# Un peu plus joli: typedef! (1/2)
+
+* Dans `opaque.h`
+
+    ```C
+    struct _table;
+    typedef struct _table * table;
+    void init(table *t);
+    void set_a(table t, int a);
+    int get_a(table t);
+    ```
+* Dans `opaque.c`
+
+    ```C
+    struct _table {
+        int a;
+    }
+    void init(table *t) {
+        *t = malloc(sizeof(**t));
+        (*t)->a = 0;
+    }
+    void set_a(table t, int a) {
+        t->a = a;
+    }
+    int get_a(table t) {
+        return t->a;
+    }
+    ```
+
+# Un peu plus joli: typedef! (2/2)
+
+* Dans `main.c`
+
+    ```C
+    int main() {
+        struct table *t = create();
+        init(&t);
+        set_a(t, 10);
+        printf("%d\n", get_a(t));
+    }
+    ```
+
+* On a fait les fonctions `get_a()` et `set_a()` comme exemples, mais...
+
+. . .
+
+* c'est pas forcément nécessaire d'implémenter (`get/set`).
+
+. . .
+
+* Par exemple, pour l'exemple de la hashmap on `get/set` les variables des structs!
+
+## Yaka
+
+* Utiliser les types opaques pour la hashmap!
+