Хабр Курсы для бэкендеров
РЕКЛАМА
Практикум, Хекслет, SkyPro, авторские курсы — собрали всех и попросили скидки. Осталось выбрать!

Просто напишу идиоматичный вариант
use std::fmt;
use std::str::FromStr;
use serde::{Deserialize, Serialize};
use thiserror::Error;
#[derive(Error, Debug)]
pub enum EmailError {
#[error("invalid email format")]
InvalidFormat,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)]
#[serde(transparent)]
pub struct Email(String);
impl Email {
pub fn new(value: impl Into<String>) -> Result<Self, EmailError> {
let s = value.into();
if s.contains('@') && s.contains('.') {
Ok(Self(s))
} else {
Err(EmailError::InvalidFormat)
}
}
pub fn as_str(&self) -> &str {
&self.0
}
}
impl fmt::Display for Email {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
impl FromStr for Email {
type Err = EmailError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::new(s)
}
}
impl AsRef<str> for Email {
fn as_ref(&self) -> &str {
&self.0
}
}
impl<'de> Deserialize<'de> for Email {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
Self::new(s).map_err(serde::de::Error::custom)
}
}Большая часть не нужна, правда.
А вот с ускорялками
use derive_more::{AsRef, Display, From};
use nutype::nutype;
#[nutype(
validate(predicate = |s| s.contains('@') && s.contains('.')),
derive(Debug, Clone, PartialEq, Eq, Hash, AsRef, Display, Serialize, Deserialize)
)]
pub struct Email(String);
// Использование
fn send(to: Email, subject: &str) {
println!("Sending '{}' to {}", subject, to);
}
fn main() -> Result<(), EmailParseError> {
let email = Email::new("user@example.com")?;
// или через parse
let email2: Email = "other@test.org".parse()?;
send(email, "Hello");
// доступ к значению
println!("{}", email2.as_ref());
Ok(())
}Или не писать derive AsRef , чтобы совсем инкапсуляция была
Паттерн Newtype и Deref-coercion в Rust