โข Option
โข Result
โข The ? operator propagates errors up the call stack concisely
โข Combinators like .map(), .and_then(), .unwrap_or() chain fallible operations
โข The compiler forces you to handle every error case โ no silent failures
โข Option
โข Result
โข The ? operator propagates errors up the call stack concisely
โข Combinators like .map(), .and_then(), .unwrap_or() chain fallible operations
โข The compiler forces you to handle every error case โ no silent failures
// 1024: File Operation Errors
// std::io::Error kinds and handling
use std::fs;
use std::io::{self, Write};
use std::path::Path;
// Approach 1: Basic file operations with io::Error
fn read_file(path: &str) -> Result<String, io::Error> {
fs::read_to_string(path)
}
fn write_file(path: &str, content: &str) -> Result<(), io::Error> {
fs::write(path, content)
}
// Approach 2: Classifying io::Error by kind
fn classify_io_error(err: &io::Error) -> &'static str {
match err.kind() {
io::ErrorKind::NotFound => "file not found",
io::ErrorKind::PermissionDenied => "permission denied",
io::ErrorKind::AlreadyExists => "already exists",
io::ErrorKind::InvalidInput => "invalid input",
io::ErrorKind::TimedOut => "timed out",
io::ErrorKind::Interrupted => "interrupted",
io::ErrorKind::WouldBlock => "would block",
_ => "other IO error",
}
}
// Approach 3: Converting io::Error to app-specific error
#[derive(Debug)]
enum FileError {
NotFound(String),
PermissionDenied(String),
Other(String),
}
impl std::fmt::Display for FileError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
FileError::NotFound(p) => write!(f, "file not found: {}", p),
FileError::PermissionDenied(p) => write!(f, "permission denied: {}", p),
FileError::Other(msg) => write!(f, "file error: {}", msg),
}
}
}
fn read_file_typed(path: &str) -> Result<String, FileError> {
fs::read_to_string(path).map_err(|e| match e.kind() {
io::ErrorKind::NotFound => FileError::NotFound(path.into()),
io::ErrorKind::PermissionDenied => FileError::PermissionDenied(path.into()),
_ => FileError::Other(e.to_string()),
})
}
// Safe file operation with existence check
fn read_if_exists(path: &str) -> Result<Option<String>, io::Error> {
if Path::new(path).exists() {
fs::read_to_string(path).map(Some)
} else {
Ok(None)
}
}
fn main() {
match read_file("/nonexistent_file_12345") {
Ok(content) => println!("Content: {}", content),
Err(e) => println!("Error: {} (kind: {})", e, classify_io_error(&e)),
}
println!("Run `cargo test` to verify all examples.");
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_read_nonexistent() {
let err = read_file("/nonexistent_file_12345").unwrap_err();
assert_eq!(err.kind(), io::ErrorKind::NotFound);
}
#[test]
fn test_classify_not_found() {
let err = io::Error::new(io::ErrorKind::NotFound, "test");
assert_eq!(classify_io_error(&err), "file not found");
}
#[test]
fn test_classify_permission() {
let err = io::Error::new(io::ErrorKind::PermissionDenied, "test");
assert_eq!(classify_io_error(&err), "permission denied");
}
#[test]
fn test_write_read_roundtrip() {
let tmp = "/tmp/rust_test_1024.txt";
write_file(tmp, "hello rust").unwrap();
let content = read_file(tmp).unwrap();
assert_eq!(content, "hello rust");
fs::remove_file(tmp).unwrap();
}
#[test]
fn test_typed_error() {
let err = read_file_typed("/nonexistent_12345").unwrap_err();
assert!(matches!(err, FileError::NotFound(_)));
assert!(err.to_string().contains("not found"));
}
#[test]
fn test_read_if_exists() {
let result = read_if_exists("/nonexistent_12345").unwrap();
assert!(result.is_none());
let tmp = "/tmp/rust_test_1024b.txt";
fs::write(tmp, "exists").unwrap();
let result = read_if_exists(tmp).unwrap();
assert_eq!(result, Some("exists".to_string()));
fs::remove_file(tmp).unwrap();
}
#[test]
fn test_io_error_display() {
let err = io::Error::new(io::ErrorKind::NotFound, "missing.txt");
assert_eq!(err.to_string(), "missing.txt");
}
#[test]
fn test_error_kind_matching() {
// io::ErrorKind is an enum โ exhaustive matching available
let err = fs::read_to_string("/no_such_file_xyz").unwrap_err();
match err.kind() {
io::ErrorKind::NotFound => {} // expected
other => panic!("unexpected error kind: {:?}", other),
}
}
}
(* 1024: File Operation Errors *)
(* Handling file I/O errors in OCaml *)
(* Approach 1: Exception-based file I/O *)
let read_file_exn path =
try
let ic = open_in path in
let content = really_input_string ic (in_channel_length ic) in
close_in ic;
content
with
| Sys_error msg -> failwith (Printf.sprintf "IO error: %s" msg)
(* Approach 2: Result-based file I/O *)
type io_error =
| FileNotFound of string
| PermissionDenied of string
| OtherIO of string
let read_file path =
try
let ic = open_in path in
let content = really_input_string ic (in_channel_length ic) in
close_in ic;
Ok content
with
| Sys_error msg when String.length msg > 0 ->
if String.sub msg 0 (min 2 (String.length msg)) = "No" then
Error (FileNotFound path)
else
Error (OtherIO msg)
let write_file path content =
try
let oc = open_out path in
output_string oc content;
close_out oc;
Ok ()
with Sys_error msg -> Error (OtherIO msg)
(* Approach 3: Safe file operations with cleanup *)
let with_file path f =
let ic = open_in path in
Fun.protect ~finally:(fun () -> close_in_noerr ic) (fun () -> f ic)
let test_exception () =
(* Can't really test file ops without temp files, so test the error path *)
(try
let _ = read_file_exn "/nonexistent_file_12345" in
assert false
with Failure msg -> assert (String.length msg > 0));
Printf.printf " Approach 1 (exception-based): passed\n"
let test_result () =
(match read_file "/nonexistent_file_12345" with
| Error _ -> ()
| Ok _ -> assert false);
Printf.printf " Approach 2 (result-based): passed\n"
let test_write_read () =
let tmp = "/tmp/ocaml_test_1024.txt" in
assert (write_file tmp "hello" = Ok ());
(match read_file tmp with
| Ok content -> assert (content = "hello")
| Error _ -> assert false);
Sys.remove tmp;
Printf.printf " Write/read round-trip: passed\n"
let () =
Printf.printf "Testing file errors:\n";
test_exception ();
test_result ();
test_write_read ();
Printf.printf "โ All tests passed\n"
| Aspect | OCaml | Rust |
|---|---|---|
| Error type | `Sys_error of string` | `io::Error` with `ErrorKind` |
| Classification | Parse error string | Match `ErrorKind` enum |
| File read | `open_in` + `really_input_string` | `fs::read_to_string` |
| Cleanup | `Fun.protect ~finally` | RAII / `Drop` trait |
| Custom errors | Wrap in variant | `map_err` to app error |
| Error info | String message only | Kind + message + OS code |