// the Event type is an enum, where its variants can carry data enum Event { // carries no data Connected, // carries one field Message(String), // carries multiple fields FileUpload { filename: String, size_bytes: usize }, // carry an enum inside another enum Error(ErrorKind), } enum ErrorKind { Network(String), Server(u16), } // function for the server to handle an event fn handle(event: Event) { // we are basically pattern matching on the 'event' parameter // we know it's of type Event, meaning we check all the possible // variants of Event, and handle them accordingly. The match also // MUST be exhaustive, if we don't explicitly handle every POSSIBLE // variant of Event, it's illegal and the code won't compile. // in other words, 'match event' is saying: // here's the 'event' variable, please check it against all these patterns, // and whichever pattern it matches, we'll evaluate that code match event { // simplest because Connected doesn't have data attached // so we can just run some code and move on Event::Connected => println!("User connected!"), // we can access the attached data and do stuff with it // btw the variable names (text, filename, etc) can be anything, // since they're only bound within these blocks Event::Message(text) => println!("Broadcast: {}", text), Event::FileUpload { filename, bytes } => { println!("Receiving file {}", filename); store_file(filename, bytes); } // since Error holds ANOTHER enum, we can match on ITS variants too // enums within enums within enums is a common pattern actually, and because // the compiler errors if we don't handle EVERY variant in EVERY match block, // it's a great way of doing this kind of branching logic safely Event::Error(kind) => match kind { ErrorKind::Network(msg) => eprintln!("Network error: {}", msg), ErrorKind::Server(code) => eprintln!("Server error code {}", code), }, } }