From ff4cb255956de7aeefed91dbaa804ffa3c28efee Mon Sep 17 00:00:00 2001 From: Serhii Potapov Date: Sat, 25 Apr 2026 17:20:34 +0200 Subject: [PATCH] Add an example with str reference --- Cargo.lock | 7 +++++ Cargo.toml | 4 ++- examples/str_reference/Cargo.toml | 7 +++++ examples/str_reference/src/main.rs | 46 ++++++++++++++++++++++++++++++ 4 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 examples/str_reference/Cargo.toml create mode 100644 examples/str_reference/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index 479f38a2..abdb53b1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -632,6 +632,13 @@ dependencies = [ "serde_core", ] +[[package]] +name = "str_reference" +version = "0.1.0" +dependencies = [ + "nutype", +] + [[package]] name = "string_arbitrary" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index f03dad09..b2b57946 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,8 @@ members = [ "examples/string_arbitrary", "examples/any_generics", "examples/custom_error", - "examples/const_example", "examples/valuable_example", + "examples/const_example", "examples/cfg_attr_example", + "examples/valuable_example", + "examples/str_reference", ] diff --git a/examples/str_reference/Cargo.toml b/examples/str_reference/Cargo.toml new file mode 100644 index 00000000..50ba0ab7 --- /dev/null +++ b/examples/str_reference/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "str_reference" +version = "0.1.0" +edition = "2024" + +[dependencies] +nutype = { path = "../../nutype" } diff --git a/examples/str_reference/src/main.rs b/examples/str_reference/src/main.rs new file mode 100644 index 00000000..fa24e0fe --- /dev/null +++ b/examples/str_reference/src/main.rs @@ -0,0 +1,46 @@ +//! Examples of nutype around references to DSTs (`&'a str`, `&'a [T]`). +//! +//! `str` and `[T]` are dynamically sized types, so they can only appear behind +//! a pointer such as `&'a str` or `&'a [T]`. nutype supports these as inner +//! types since v0.6.0 (`&'a str`) and via generics for slice references. + +use nutype::nutype; + +// A non-empty trimmed name borrowed from somewhere else. +#[nutype( + validate(predicate = |s| !s.trim().is_empty()), + derive(Debug, Clone, Copy, PartialEq, AsRef), +)] +pub struct Name<'a>(&'a str); + +// A non-empty slice of any T, useful for read-only views of validated data. +#[nutype( + validate(predicate = |s| !s.is_empty()), + derive(Debug, Clone, Copy, PartialEq), +)] +pub struct NonEmptySlice<'a, T>(&'a [T]); + +fn main() { + // &'a str: borrowed string with a non-empty invariant. + let raw = String::from(" Alice "); + let name = Name::try_new(&raw).unwrap(); + assert_eq!(name.as_ref(), &" Alice "); + + // Whitespace-only input is rejected. + assert!(Name::try_new(" ").is_err()); + + // A &'static str works just as well. + let constant: Name<'static> = Name::try_new("Bob").unwrap(); + assert_eq!(constant.as_ref(), &"Bob"); + + // &'a [T]: borrowed slice that must be non-empty. + let numbers = vec![1, 2, 3]; + let slice = NonEmptySlice::try_new(numbers.as_slice()).unwrap(); + assert_eq!(slice.into_inner(), &[1, 2, 3]); + + // An empty slice is rejected. + let empty: &[i32] = &[]; + assert!(NonEmptySlice::try_new(empty).is_err()); + + println!("str_reference example: all assertions passed"); +}