pub struct Tagged<T, Tag> { /* private fields */ }Expand description
rust-tagged provides a simple way to define strongly typed wrappers over primitive types like String, i32, Uuid, chrono::DateTime, etc. It helps eliminate bugs caused by misusing raw primitives for conceptually distinct fields such as UserId, Email, ProductId, and more.
Eliminate accidental mixups between similar types (e.g. OrgId vs UserId) Enforce domain modeling in code via the type system Ergonomic .into() support for primitive conversions
§Example - Simple
use tagged_core::{Tagged};
#[derive(Debug)]
struct EmailTag;
type Email = Tagged<String, EmailTag>;
fn main() {
let email: Email = "test@example.com".into();
println!("Email inner value: {}", email.value());
// Convert back to String
let raw: String = email.into();
println!("Raw String: {raw}");
}Implementations§
Source§impl<T, Tag> Tagged<T, Tag>
impl<T, Tag> Tagged<T, Tag>
pub fn new(value: T) -> Tagged<T, Tag>
Sourcepub fn value(&self) -> &T
👎Deprecated since 0.8.0: Using .value() breaks type safety. Prefer Deref coercion (&*tagged) or Into conversion (tagged.into()) instead.
pub fn value(&self) -> &T
⚠️ WARNING: Avoid using .value() as it breaks type safety!
Using .value() extracts the inner value, which defeats the purpose of Tagged types.
Instead, prefer:
- Using
Derefcoercion:let raw: &T = &*tagged; - Using
Intoconversion:let raw: T = tagged.into(); - Keeping values as
Taggedtypes throughout your codebase
§Example (Avoid)
let user_id: UserId = 42.into();
let raw = user_id.value(); // ⚠️ Breaks type safety!§Example (Preferred)
let user_id: UserId = 42.into();
let raw: u32 = user_id.into(); // ✓ Maintains type safety through conversionSource§impl<T, Tag> Tagged<T, Tag>where
T: DeserializeOwned,
Support JSON string deserialization into Tagged<T, Tag>
impl<T, Tag> Tagged<T, Tag>where
T: DeserializeOwned,
Support JSON string deserialization into Tagged<T, Tag>
§Example
use tagged_core::Tagged;
use serde::Deserialize;
use std::convert::TryFrom;
#[derive(Debug, Deserialize)]
struct UserIdTag;
type UserId = Tagged<u32, UserIdTag>;
fn main() {
let json = "42";
let user_id: UserId = Tagged::from_json(json).unwrap();
println!("User ID: {}", user_id.value());
}Sourcepub fn from_json(json: &str) -> Result<Tagged<T, Tag>, Error>
pub fn from_json(json: &str) -> Result<Tagged<T, Tag>, Error>
Deserialize a JSON string into a Tagged type
Requires the serde feature to be enabled.
§Errors
Returns a serde_json::Error if the JSON string cannot be deserialized into type T
§Example
use tagged_core::Tagged;
use serde::Deserialize;
#[derive(Debug, Deserialize)]
struct UserIdTag;
type UserId = Tagged<u32, UserIdTag>;
fn main() {
let json = "42";
let user_id: UserId = Tagged::from_json(json).unwrap();
println!("User ID: {}", user_id.value());
}Sourcepub fn from_json_string(json: String) -> Result<Tagged<T, Tag>, Error>
pub fn from_json_string(json: String) -> Result<Tagged<T, Tag>, Error>
Deserialize a JSON string from a String into a Tagged type
Requires the serde feature to be enabled.
§Errors
Returns a serde_json::Error if the JSON string cannot be deserialized into type T
§Example
use tagged_core::Tagged;
use serde::Deserialize;
#[derive(Debug, Deserialize)]
struct UserIdTag;
type UserId = Tagged<u32, UserIdTag>;
fn main() {
let json = String::from("42");
let user_id: UserId = Tagged::from_json_string(json).unwrap();
println!("User ID: {}", user_id.value());
}Source§impl<T, Tag> Tagged<T, Tag>where
T: Serialize,
impl<T, Tag> Tagged<T, Tag>where
T: Serialize,
Sourcepub fn to_json(&self) -> Result<String, Error>
pub fn to_json(&self) -> Result<String, Error>
Serialize a Tagged type into a JSON string
Requires the serde feature to be enabled.
§Errors
Returns a serde_json::Error if the value cannot be serialized to JSON
§Example
use tagged_core::Tagged;
use serde::Serialize;
#[derive(Debug, Serialize)]
struct UserIdTag;
type UserId = Tagged<u32, UserIdTag>;
fn main() {
let user_id: UserId = Tagged::from(42);
let json = user_id.to_json().unwrap();
println!("JSON: {}", json);
}Sourcepub fn to_json_pretty(&self) -> Result<String, Error>
pub fn to_json_pretty(&self) -> Result<String, Error>
Serialize a Tagged type into a pretty-printed JSON string
Requires the serde feature to be enabled.
§Errors
Returns a serde_json::Error if the value cannot be serialized to JSON
§Example
use tagged_core::Tagged;
use serde::Serialize;
#[derive(Debug, Serialize)]
struct UserIdTag;
type UserId = Tagged<u32, UserIdTag>;
fn main() {
let user_id: UserId = Tagged::from(42);
let json = user_id.to_json_pretty().unwrap();
println!("JSON: {}", json);
}Trait Implementations§
Source§impl<T, Tag> Debug for Tagged<T, Tag>where
T: Debug,
§Example - Debug
use tagged_core::Tagged;
#[derive(Debug)]
struct UserIdTag {
a: Tagged<u32, Self>,
b: Tagged<u32, Self>,
}
fn main() {
let instance = UserIdTag{a: 1.into(), b: 2.into()};
println!("{}", instance.a);
println!("{:?}", instance.b);
}
impl<T, Tag> Debug for Tagged<T, Tag>where
T: Debug,
§Example - Debug
use tagged_core::Tagged;
#[derive(Debug)]
struct UserIdTag {
a: Tagged<u32, Self>,
b: Tagged<u32, Self>,
}
fn main() {
let instance = UserIdTag{a: 1.into(), b: 2.into()};
println!("{}", instance.a);
println!("{:?}", instance.b);
}Source§impl<'de, T, Tag> Deserialize<'de> for Tagged<T, Tag>where
T: Deserialize<'de>,
Available on crate feature serde only.
use serde::{Deserialize, Serialize};
use tagged_core::Tagged;
#[derive(Clone, Hash, Debug, PartialEq, Eq, Serialize, Deserialize)]
struct SomeCustomType {
some_id: String
}
#[derive(Clone, Hash, Debug, PartialEq, Eq, Serialize, Deserialize)]
struct SomeCustomType2(String);
#[derive(Clone, Hash, Debug, PartialEq, Eq, Serialize, Deserialize)]
struct User {
id: Tagged<String, Self>,
id2: SomeCustomType,
id3: SomeCustomType2,
}
fn main() {
let user = User { id: "1".into() , id2: SomeCustomType { some_id: "2".into() }, id3: SomeCustomType2("3".into())};
let j = serde_json::to_string(&user).unwrap();
let converted_user = serde_json::from_str::<User>(&j).unwrap();
println!("{}", j);
println!("{:?}", converted_user);
}
/*
Running `target/debug/examples/Serde_example`
{"id":"1","id2":{"some_id":"2"},"id3":"3"}
User { id: "1", id2: SomeCustomType { some_id: "2" }, id3: SomeCustomType2("3") }
Process finished with exit code 0
*/
/*
Problem with normal types
{"id":"1","id2":{"some_id":"2"}}
// rust is powerful enough to solve it using touple
{"id":"1","id2":{"some_id":"2"},"id3":"3"}
// or we can use a new type called tagged that don't need a new name.
*/
impl<'de, T, Tag> Deserialize<'de> for Tagged<T, Tag>where
T: Deserialize<'de>,
serde only.use serde::{Deserialize, Serialize};
use tagged_core::Tagged;
#[derive(Clone, Hash, Debug, PartialEq, Eq, Serialize, Deserialize)]
struct SomeCustomType {
some_id: String
}
#[derive(Clone, Hash, Debug, PartialEq, Eq, Serialize, Deserialize)]
struct SomeCustomType2(String);
#[derive(Clone, Hash, Debug, PartialEq, Eq, Serialize, Deserialize)]
struct User {
id: Tagged<String, Self>,
id2: SomeCustomType,
id3: SomeCustomType2,
}
fn main() {
let user = User { id: "1".into() , id2: SomeCustomType { some_id: "2".into() }, id3: SomeCustomType2("3".into())};
let j = serde_json::to_string(&user).unwrap();
let converted_user = serde_json::from_str::<User>(&j).unwrap();
println!("{}", j);
println!("{:?}", converted_user);
}
/*
Running `target/debug/examples/Serde_example`
{"id":"1","id2":{"some_id":"2"},"id3":"3"}
User { id: "1", id2: SomeCustomType { some_id: "2" }, id3: SomeCustomType2("3") }
Process finished with exit code 0
*/
/*
Problem with normal types
{"id":"1","id2":{"some_id":"2"}}
// rust is powerful enough to solve it using touple
{"id":"1","id2":{"some_id":"2"},"id3":"3"}
// or we can use a new type called tagged that don't need a new name.
*/Source§fn deserialize<D>(
deserializer: D,
) -> Result<Tagged<T, Tag>, <D as Deserializer<'de>>::Error>where
D: Deserializer<'de>,
fn deserialize<D>(
deserializer: D,
) -> Result<Tagged<T, Tag>, <D as Deserializer<'de>>::Error>where
D: Deserializer<'de>,
Source§impl<T, Tag> Hash for Tagged<T, Tag>where
T: Hash,
§Example - Hash
fn main() {
use tagged_core::Tagged;
use std::collections::HashSet;
#[derive(Clone, Hash, Debug, PartialEq, Eq)]
struct User {
id: Tagged<String, Self>
}
let mut s: HashSet<User> = HashSet::new();
let user = User{id: "me@example.com".into()};
s.insert(user.clone());
assert!(s.contains(&user));
}
impl<T, Tag> Hash for Tagged<T, Tag>where
T: Hash,
§Example - Hash
fn main() {
use tagged_core::Tagged;
use std::collections::HashSet;
#[derive(Clone, Hash, Debug, PartialEq, Eq)]
struct User {
id: Tagged<String, Self>
}
let mut s: HashSet<User> = HashSet::new();
let user = User{id: "me@example.com".into()};
s.insert(user.clone());
assert!(s.contains(&user));
}Source§impl<'a, T, Tag> IntoIterator for &'a Tagged<Vec<T>, Tag>
use tagged_core::Tagged;
#[derive(Debug)]
struct Org;
type EmployeeNames = Tagged<Vec<String>, Org>;
fn main() {
let names: EmployeeNames = Tagged::new(vec!["Alice".into(), "Bob".into()]);
names.iter().for_each(|name| println!("Name: {}", name));
}
/*
Name: Alice
Name: Bob
*/
impl<'a, T, Tag> IntoIterator for &'a Tagged<Vec<T>, Tag>
use tagged_core::Tagged;
#[derive(Debug)]
struct Org;
type EmployeeNames = Tagged<Vec<String>, Org>;
fn main() {
let names: EmployeeNames = Tagged::new(vec!["Alice".into(), "Bob".into()]);
names.iter().for_each(|name| println!("Name: {}", name));
}
/*
Name: Alice
Name: Bob
*/Source§impl<T, Tag> IntoIterator for Tagged<Vec<T>, Tag>
use tagged_core::Tagged;
#[derive(Debug)]
struct Org;
type EmployeeNames = Tagged<Vec<String>, Org>;
fn main() {
let names: EmployeeNames = Tagged::new(vec!["Alice".into(), "Bob".into()]);
names.into_iter().for_each(|name| println!("Name: {}", name));
}
/*
Name: Alice
Name: Bob
*/
impl<T, Tag> IntoIterator for Tagged<Vec<T>, Tag>
use tagged_core::Tagged;
#[derive(Debug)]
struct Org;
type EmployeeNames = Tagged<Vec<String>, Org>;
fn main() {
let names: EmployeeNames = Tagged::new(vec!["Alice".into(), "Bob".into()]);
names.into_iter().for_each(|name| println!("Name: {}", name));
}
/*
Name: Alice
Name: Bob
*/Source§impl<T, Tag> Ord for Tagged<T, Tag>where
T: Ord,
impl<T, Tag> Ord for Tagged<T, Tag>where
T: Ord,
1.21.0 · Source§fn max(self, other: Self) -> Selfwhere
Self: Sized,
fn max(self, other: Self) -> Selfwhere
Self: Sized,
Source§impl<T, Tag> PartialOrd for Tagged<T, Tag>where
T: PartialOrd,
impl<T, Tag> PartialOrd for Tagged<T, Tag>where
T: PartialOrd,
Source§impl<T, Tag> Serialize for Tagged<T, Tag>where
T: Serialize,
Available on crate feature serde only.Example - Serialize
impl<T, Tag> Serialize for Tagged<T, Tag>where
T: Serialize,
serde only.Example - Serialize
use serde::{Deserialize, Serialize};
use tagged_core::Tagged;
#[derive(Clone, Hash, Debug, PartialEq, Eq, Serialize, Deserialize)]
struct SomeCustomType {
some_id: String
}
#[derive(Clone, Hash, Debug, PartialEq, Eq, Serialize, Deserialize)]
struct SomeCustomType2(String);
#[derive(Clone, Hash, Debug, PartialEq, Eq, Serialize, Deserialize)]
struct User {
id: Tagged<String, Self>,
id2: SomeCustomType,
id3: SomeCustomType2,
}
fn main() {
let user = User { id: "1".into() , id2: SomeCustomType { some_id: "2".into() }, id3: SomeCustomType2("3".into())};
let j = serde_json::to_string(&user).unwrap();
println!("{}", j);
}
/*
Problem with normal types
{"id":"1","id2":{"some_id":"2"}}
// rust is powerful enough to solve it using touple
{"id":"1","id2":{"some_id":"2"},"id3":"3"}
// or we can use a new type called tagged that don't need a new name.
*/