// Associated type bounds: traits with associated types constrained by other traits.
//
// OCaml uses `with type key = int` module sharing constraints.
// Rust uses `type Key: Ord + Clone + Display` in trait definitions โ
// the associated type itself carries bounds.
//
// This lets generic code reason about the key type without knowing its concrete form.
use std::fmt::{Debug, Display};
// โโ Core Collection trait with bounded associated types โโโโโโโโโโโโโโโโโโโโโ
pub trait Collection: Sized {
/// The key type must be orderable, cloneable, and printable.
type Key: Ord + Clone + Display + Debug;
type Value: Clone + Debug;
fn empty() -> Self;
fn insert(self, key: Self::Key, value: Self::Value) -> Self;
fn find(&self, key: &Self::Key) -> Option<&Self::Value>;
fn size(&self) -> usize;
fn is_empty(&self) -> bool { self.size() == 0 }
/// Remove a key โ provided default that rebuilds the collection.
fn remove(self, key: &Self::Key) -> Self;
/// Collect all keys in sorted order (thanks to the Ord bound).
fn sorted_keys(&self) -> Vec<Self::Key>;
}
// โโ Integer-keyed map โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
#[derive(Debug, Clone)]
pub struct IntMap<V> {
entries: Vec<(i32, V)>,
}
impl<V: Clone + Debug> Collection for IntMap<V> {
type Key = i32;
type Value = V;
fn empty() -> Self { IntMap { entries: Vec::new() } }
fn insert(mut self, key: i32, value: V) -> Self {
self.entries.retain(|(k, _)| *k != key);
self.entries.push((key, value));
self
}
fn find(&self, key: &i32) -> Option<&V> {
self.entries.iter().find(|(k, _)| k == key).map(|(_, v)| v)
}
fn size(&self) -> usize { self.entries.len() }
fn remove(mut self, key: &i32) -> Self {
self.entries.retain(|(k, _)| k != key);
self
}
fn sorted_keys(&self) -> Vec<i32> {
let mut keys: Vec<i32> = self.entries.iter().map(|(k, _)| *k).collect();
keys.sort();
keys
}
}
// โโ String-keyed map โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
#[derive(Debug, Clone)]
pub struct StringMap<V> {
entries: Vec<(String, V)>,
}
impl<V: Clone + Debug> Collection for StringMap<V> {
type Key = String;
type Value = V;
fn empty() -> Self { StringMap { entries: Vec::new() } }
fn insert(mut self, key: String, value: V) -> Self {
self.entries.retain(|(k, _)| k != &key);
self.entries.push((key, value));
self
}
fn find(&self, key: &String) -> Option<&V> {
self.entries.iter().find(|(k, _)| k == key).map(|(_, v)| v)
}
fn size(&self) -> usize { self.entries.len() }
fn remove(mut self, key: &String) -> Self {
self.entries.retain(|(k, _)| k != key);
self
}
fn sorted_keys(&self) -> Vec<String> {
let mut keys: Vec<String> = self.entries.iter().map(|(k, _)| k.clone()).collect();
keys.sort();
keys
}
}
// โโ Generic functions that work with any Collection โโโโโโโโโโโโโโโโโโโโโโโโโโ
/// Print all keys in sorted order โ works because Key: Display + Ord.
fn print_sorted_keys<C: Collection>(coll: &C) {
let keys = coll.sorted_keys();
let strs: Vec<String> = keys.iter().map(|k| format!("{}", k)).collect();
println!(" keys (sorted): [{}]", strs.join(", "));
}
/// Look up and display a value.
fn lookup_display<C: Collection>(coll: &C, key: &C::Key)
where
C::Value: Display,
{
match coll.find(key) {
Some(v) => println!(" find({}) = {}", key, v),
None => println!(" find({}) = not found", key),
}
}
/// Merge two collections of the same type (second wins on key conflict).
/// Note: a full implementation would copy values; this shows the type structure.
#[allow(dead_code)]
fn merge_keys<C: Collection>(a: C, b: C) -> Vec<C::Key> {
let mut keys = a.sorted_keys();
let bkeys = b.sorted_keys();
for k in bkeys {
if !keys.contains(&k) {
keys.push(k);
}
}
keys.sort();
keys
}
fn main() {
// Integer-keyed map
let m = IntMap::<&str>::empty()
.insert(2, "two")
.insert(1, "one")
.insert(3, "three");
println!("IntMap size: {}", m.size());
print_sorted_keys(&m);
lookup_display(&m, &1);
lookup_display(&m, &4);
// Remove a key
let m2 = m.remove(&2);
println!("After remove(2), size: {}", m2.size());
print_sorted_keys(&m2);
// String-keyed map
let sm = StringMap::<i32>::empty()
.insert("banana".to_string(), 3)
.insert("apple".to_string(), 1)
.insert("cherry".to_string(), 2);
println!("StringMap size: {}", sm.size());
print_sorted_keys(&sm);
lookup_display(&sm, &"apple".to_string());
// Generic function: the Key bound lets us sort without knowing the concrete type
println!("Both maps sorted correctly via the Ord bound on associated type Key.");
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_int_map_insert_find() {
let m = IntMap::<&str>::empty()
.insert(1, "one")
.insert(2, "two");
assert_eq!(m.size(), 2);
assert_eq!(m.find(&1), Some(&"one"));
assert_eq!(m.find(&3), None);
}
#[test]
fn test_int_map_sorted_keys() {
let m = IntMap::<i32>::empty()
.insert(3, 0)
.insert(1, 0)
.insert(2, 0);
assert_eq!(m.sorted_keys(), vec![1, 2, 3]);
}
#[test]
fn test_string_map() {
let m = StringMap::<i32>::empty()
.insert("b".to_string(), 2)
.insert("a".to_string(), 1);
assert_eq!(m.find(&"a".to_string()), Some(&1));
assert_eq!(m.sorted_keys(), vec!["a".to_string(), "b".to_string()]);
}
#[test]
fn test_remove() {
let m = IntMap::<i32>::empty()
.insert(1, 10)
.insert(2, 20)
.remove(&1);
assert_eq!(m.size(), 1);
assert_eq!(m.find(&1), None);
}
#[test]
fn test_overwrite() {
let m = IntMap::<i32>::empty()
.insert(1, 10)
.insert(1, 99);
assert_eq!(m.size(), 1);
assert_eq!(m.find(&1), Some(&99));
}
}
(* OCaml's "associated type bounds" are expressed via module type constraints.
A module type can constrain its type members with :=, with type, or sharing. *)
module type COLLECTION = sig
type 'a t
type key
val empty : 'a t
val insert : key -> 'a -> 'a t -> 'a t
val find : key -> 'a t -> 'a option
val size : 'a t -> int
end
(* Integer-keyed map *)
module IntMap : COLLECTION with type key = int = struct
type key = int
type 'a t = (int * 'a) list
let empty = []
let insert k v m = (k, v) :: List.filter (fun (k', _) -> k' <> k) m
let find k m = List.assoc_opt k m
let size m = List.length m
end
(* String-keyed map *)
module StringMap : COLLECTION with type key = string = struct
type key = string
type 'a t = (string * 'a) list
let empty = []
let insert k v m = (k, v) :: List.filter (fun (k', _) -> k' <> k) m
let find k m = List.assoc_opt k m
let size m = List.length m
end
let () =
let m = IntMap.(insert 1 "one" (insert 2 "two" empty)) in
Printf.printf "IntMap size: %d\n" (IntMap.size m);
(match IntMap.find 1 m with
| Some v -> Printf.printf "Found: %s\n" v
| None -> assert false);
let sm = StringMap.(insert "a" 1 (insert "b" 2 empty)) in
Printf.printf "StringMap size: %d\n" (StringMap.size sm)