// 1023: Safe Integer Parsing
// str::parse::<i64>() and handling ParseIntError
use std::num::ParseIntError;
// Approach 1: Basic parse with Result
fn parse_int(s: &str) -> Result<i64, ParseIntError> {
s.parse::<i64>()
}
// Approach 2: Parse with custom error message
fn parse_int_msg(s: &str) -> Result<i64, String> {
s.parse::<i64>()
.map_err(|e| format!("cannot parse '{}' as integer: {}", s, e))
}
// Approach 3: Parse with validation
fn parse_positive(s: &str) -> Result<i64, String> {
let n: i64 = s.parse().map_err(|_| format!("not a number: {}", s))?;
if n < 0 {
Err(format!("negative: {}", n))
} else {
Ok(n)
}
}
fn parse_in_range(s: &str, min: i64, max: i64) -> Result<i64, String> {
let n: i64 = s.parse().map_err(|_| format!("not a number: {}", s))?;
if n < min {
Err(format!("{} < min({})", n, min))
} else if n > max {
Err(format!("{} > max({})", n, max))
} else {
Ok(n)
}
}
// Parse with default (Option-based)
fn parse_or_default(s: &str, default: i64) -> i64 {
s.parse::<i64>().unwrap_or(default)
}
fn main() {
println!("parse '42': {:?}", parse_int("42"));
println!("parse 'abc': {:?}", parse_int("abc"));
println!("parse_positive '-5': {:?}", parse_positive("-5"));
println!("parse_in_range '50' [1,100]: {:?}", parse_in_range("50", 1, 100));
println!("Run `cargo test` to verify all examples.");
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_basic_parse() {
assert_eq!(parse_int("42"), Ok(42));
assert_eq!(parse_int("-17"), Ok(-17));
assert_eq!(parse_int("0"), Ok(0));
}
#[test]
fn test_parse_errors() {
assert!(parse_int("abc").is_err());
assert!(parse_int("").is_err());
assert!(parse_int("12.5").is_err()); // no floats
assert!(parse_int("99999999999999999999").is_err()); // overflow
}
#[test]
fn test_parse_with_message() {
let err = parse_int_msg("abc").unwrap_err();
assert!(err.contains("cannot parse"));
assert!(err.contains("abc"));
}
#[test]
fn test_parse_positive() {
assert_eq!(parse_positive("42"), Ok(42));
assert_eq!(parse_positive("0"), Ok(0));
assert!(parse_positive("-5").unwrap_err().contains("negative"));
assert!(parse_positive("xyz").unwrap_err().contains("not a number"));
}
#[test]
fn test_parse_in_range() {
assert_eq!(parse_in_range("50", 1, 100), Ok(50));
assert_eq!(parse_in_range("1", 1, 100), Ok(1));
assert_eq!(parse_in_range("100", 1, 100), Ok(100));
assert!(parse_in_range("0", 1, 100).is_err());
assert!(parse_in_range("101", 1, 100).is_err());
assert!(parse_in_range("abc", 1, 100).is_err());
}
#[test]
fn test_parse_or_default() {
assert_eq!(parse_or_default("42", 0), 42);
assert_eq!(parse_or_default("abc", 0), 0);
assert_eq!(parse_or_default("", -1), -1);
}
#[test]
fn test_parse_int_error_kind() {
// ParseIntError has useful information
let err = "abc".parse::<i64>().unwrap_err();
assert_eq!(err.to_string(), "invalid digit found in string");
let err = "".parse::<i64>().unwrap_err();
assert_eq!(err.to_string(), "cannot parse integer from empty string");
}
#[test]
fn test_whitespace_handling() {
// Rust's parse does NOT trim whitespace
assert!(parse_int(" 42").is_err());
assert!(parse_int("42 ").is_err());
// Trim first if needed
assert_eq!(" 42 ".trim().parse::<i64>(), Ok(42));
}
}
(* 1023: Safe Integer Parsing *)
(* OCaml int_of_string_opt vs exception-based parsing *)
(* Approach 1: Exception-based (old style) *)
let parse_exn s =
try int_of_string s
with Failure _ -> 0 (* silent default โ BAD *)
(* Approach 2: Option-based (safe) *)
let parse_opt s = int_of_string_opt s
let parse_or_default default s =
match int_of_string_opt s with
| Some n -> n
| None -> default
(* Approach 3: Result-based with error message *)
let parse_result s =
match int_of_string_opt s with
| Some n -> Ok n
| None -> Error (Printf.sprintf "cannot parse '%s' as integer" s)
let parse_positive s =
match int_of_string_opt s with
| None -> Error (Printf.sprintf "not a number: %s" s)
| Some n when n < 0 -> Error (Printf.sprintf "negative: %d" n)
| Some n -> Ok n
(* Parse with range validation *)
let parse_in_range ~min ~max s =
match int_of_string_opt s with
| None -> Error (Printf.sprintf "not a number: %s" s)
| Some n when n < min -> Error (Printf.sprintf "%d < min(%d)" n min)
| Some n when n > max -> Error (Printf.sprintf "%d > max(%d)" n max)
| Some n -> Ok n
let test_exception () =
assert (parse_exn "42" = 42);
assert (parse_exn "abc" = 0); (* silent failure! *)
Printf.printf " Approach 1 (exception, unsafe): passed\n"
let test_option () =
assert (parse_opt "42" = Some 42);
assert (parse_opt "abc" = None);
assert (parse_opt "" = None);
assert (parse_or_default 0 "abc" = 0);
assert (parse_or_default 0 "42" = 42);
Printf.printf " Approach 2 (option): passed\n"
let test_result () =
assert (parse_result "42" = Ok 42);
(match parse_result "abc" with Error _ -> () | Ok _ -> assert false);
assert (parse_positive "42" = Ok 42);
assert (parse_positive "-5" = Error "negative: -5");
assert (parse_in_range ~min:1 ~max:100 "50" = Ok 50);
assert (parse_in_range ~min:1 ~max:100 "0" |> Result.is_error);
assert (parse_in_range ~min:1 ~max:100 "101" |> Result.is_error);
Printf.printf " Approach 3 (result): passed\n"
let () =
Printf.printf "Testing safe integer parsing:\n";
test_exception ();
test_option ();
test_result ();
Printf.printf "โ All tests passed\n"