Библиотека lettre для Rust: отправка email
1. Что это и зачем нужна
lettre (с фр. "письмо") — это мощная, асинхронная и типобезопасная библиотека для отправки электронных писем на языке Rust. Она предоставляет полный набор инструментов для создания и отправки email-сообщений, поддерживая как простые текстовые письма, так и сложные HTML-письма с вложениями. Библиотека написана на чистом Rust без внешних зависимостей на системном уровне, что делает её идеальным выбором для проектов, где важна переносимость и безопасность.
Зачем нужна lettre? В современной веб-разработке отправка email остаётся критически важной функцией: регистрация пользователей, сброс паролей, уведомления, рассылки. Rust, будучи системным языком, традиционно не имел удобных средств для работы с почтой. lettre заполняет этот пробел, предлагая элегантный API, полную поддержку стандартов SMTP, MIME и SSL/TLS, а также интеграцию с популярными асинхронными рантаймами (tokio, async-std).
Ключевые возможности библиотеки включают: поддержку SMTP, SMTPS и SMTP через STARTTLS; создание многосоставных MIME-сообщений с вложениями; работу с HTML и текстовыми частями; поддержку асинхронной отправки; автоматическое кодирование вложений и заголовков; а также полную безопасность типов на этапе компиляции, что предотвращает многие ошибки, характерные для динамических языков.
2. Установка
Для установки библиотеки lettre добавьте её в файл Cargo.toml вашего проекта:
[dependencies]
lettre = "0.11"
Для асинхронной работы с tokio (рекомендуется):
[dependencies]
lettre = { version = "0.11", features = ["tokio1-rustls-tls", "builder"] }
tokio = { version = "1", features = ["full"] }
Для синхронной работы (без асинхронного рантайма):
[dependencies]
lettre = { version = "0.11", features = ["rustls-tls", "builder"] }
После добавления зависимостей выполните команду:
cargo build
3. Быстрый старт
Минимальный рабочий пример отправки email через SMTP-сервер Gmail:
use lettre::message::header::ContentType;
use lettre::transport::smtp::authentication::Credentials;
use lettre::{Message, SmtpTransport, Transport};
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Создаём письмо
let email = Message::builder()
.from("Отправитель <your_email@gmail.com>".parse()?)
.to("Получатель <recipient@example.com>".parse()?)
.subject("Привет из Rust!")
.header(ContentType::TEXT_PLAIN)
.body("Это тестовое письмо, отправленное с помощью lettre.".to_string())?;
// Настройка SMTP-транспорта
let creds = Credentials::new(
"your_email@gmail.com".to_string(),
"your_app_password".to_string() // Используйте пароль приложения
);
let mailer = SmtpTransport::relay("smtp.gmail.com")?
.credentials(creds)
.build();
// Отправка
match mailer.send(&email) {
Ok(_) => println!("Письмо отправлено успешно!"),
Err(e) => eprintln!("Ошибка отправки: {:?}", e),
}
Ok(())
}
4. Основные методы и структуры
4.1. Message::builder()
Сигнатура: pub fn builder() -> MessageBuilder
Описание: Создаёт новый билдер для построения email-сообщения. Билдер позволяет цепочкой методов задать все параметры письма: отправителя, получателей, тему, содержимое и заголовки.
use lettre::Message;
let email = Message::builder()
.from("user@example.com".parse()?)
.to("recipient@example.com".parse()?)
.subject("Тест")
.body("Привет!".to_string())?;
4.2. MessageBuilder::to()
Сигнатура: pub fn to(self, addr: Address) -> MessageBuilder
Описание: Добавляет получателя письма. Можно вызывать несколько раз для добавления нескольких получателей. Принимает объект типа Address, который парсится из строки.
use lettre::Message;
let email = Message::builder()
.from("from@example.com".parse()?)
.to("first@example.com".parse()?)
.to("second@example.com".parse()?)
.subject("Несколько получателей")
.body("Это письмо отправлено нескольким людям.".to_string())?;
4.3. MessageBuilder::cc() и bcc()
Сигнатура: pub fn cc(self, addr: Address) -> MessageBuilder / pub fn bcc(self, addr: Address) -> MessageBuilder
Описание: Добавляет получателей в копию (CC) или скрытую копию (BCC). Работают аналогично методу to().
use lettre::Message;
let email = Message::builder()
.from("from@example.com".parse()?)
.to("main@example.com".parse()?)
.cc("cc@example.com".parse()?)
.bcc("bcc@example.com".parse()?)
.subject("С копией и скрытой копией")
.body("Текст письма.".to_string())?;
4.4. MessageBuilder::header()
Сигнатура: pub fn header(self, header: Header) -> MessageBuilder
Описание: Добавляет произвольный заголовок к письму. Позволяет задать Content-Type, Reply-To, Message-ID и другие заголовки.
use lettre::message::header::ContentType;
use lettre::Message;
let email = Message::builder()
.from("from@example.com".parse()?)
.to("to@example.com".parse()?)
.subject("HTML письмо")
.header(ContentType::TEXT_HTML)
.body("<h1>Привет!</h1><p>Это HTML письмо.</p>".to_string())?;
4.5. MessageBuilder::singlepart() и multipart()
Сигнатура: pub fn singlepart(self, body: SinglePart) -> Result<Message> / pub fn multipart(self, body: MultiPart) -> Result<Message>
Описание: Создают письмо с одной частью (singlepart) или несколькими частями (multipart). Multipart используется для писем с вложениями или смешанным содержимым (текст + HTML).
use lettre::message::{MultiPart, SinglePart};
use lettre::Message;
// Письмо с двумя альтернативами: текст и HTML
let email = Message::builder()
.from("from@example.com".parse()?)
.to("to@example.com".parse()?)
.subject("Альтернативный контент")
.multipart(
MultiPart::alternative()
.singlepart(
SinglePart::builder()
.header(lettre::message::header::ContentType::TEXT_PLAIN)
.body("Текстовая версия".to_string())
)
.singlepart(
SinglePart::builder()
.header(lettre::message::header::ContentType::TEXT_HTML)
.body("<h1>HTML версия</h1>".to_string())
)
)?;
4.6. SmtpTransport::relay()
Сигнатура: pub fn relay(relay: &str) -> Result<SmtpTransportBuilder>
Описание: Создаёт транспорт для отправки через указанный SMTP-релей (сервер). Автоматически определяет порт и протокол (обычно 587 для STARTTLS).
use lettre::transport::smtp::authentication::Credentials;
use lettre::{SmtpTransport, Transport};
let creds = Credentials::new("user".to_string(), "pass".to_string());
let mailer = SmtpTransport::relay("smtp.gmail.com")?
.credentials(creds)
.build();
4.7. SmtpTransport::starttls() и SmtpTransport::ssl()
Сигнатура: pub fn starttls(self, config: Tls) -> SmtpTransportBuilder / pub fn ssl(self, config: Tls) -> SmtpTransportBuilder
Описание: Настраивают режимы шифрования: STARTTLS (обновление соединения до шифрованного) или прямое SSL-соединение. Позволяют задать обязательность TLS.
use lettre::transport::smtp::Tls;
use lettre::SmtpTransport;
// Обязательный STARTTLS
let mailer = SmtpTransport::relay("smtp.example.com")?
.tls(Tls::Required(
lettre::transport::smtp::client::TlsParameters::new("smtp.example.com".to_string())?
))
.build();
// Прямое SSL-соединение на порту 465
let mailer = SmtpTransport::builder_dangerous("smtp.example.com")
.port(465)
.tls(Tls::Wrapper(
lettre::transport::smtp::client::TlsParameters::new("smtp.example.com".to_string())?
))
.build();
4.8. Transport::send()
Сигнатура: fn send(&self, message: &Message) -> Result<Response>
Описание: Отправляет подготовленное сообщение через транспорт. Возвращает Response с информацией о результате отправки.
use lettre::transport::smtp::authentication::Credentials;
use lettre::{Message, SmtpTransport, Transport};
let email = Message::builder()
.from("from@example.com".parse()?)
.to("to@example.com".parse()?)
.subject("Тест")
.body("Тело письма".to_string())?;
let mailer = SmtpTransport::relay("smtp.gmail.com")?
.credentials(Credentials::new("user".to_string(), "pass".to_string()))
.build();
match mailer.send(&email) {
Ok(response) => println!("Отправлено: {:?}", response),
Err(e) => eprintln!("Ошибка: {:?}", e),
}
5. Реальный практический пример
Ниже представлен полноценный пример отправки email с HTML-шаблоном, вложением и использованием асинхронного рантайма tokio:
use lettre::message::{header::ContentType, Attachment, MultiPart, SinglePart};
use lettre::transport::smtp::authentication::Credentials;
use lettre::{AsyncSmtpTransport, AsyncTransport, Message, Tokio1Executor};
use std::env;
/// Структура для данных письма
struct EmailData {
to: String,
subject: String,
username: String,
confirmation_link: String,
}
/// Генерация HTML-шаблона письма
fn build_html_body(data: &EmailData) -> String {
format!(
r#"<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"></head>
<body style="font-family: Arial, sans-serif; background: #f4f4f4; padding: 20px;">
<div style="max-width: 600px; margin: 0 auto; background: white; padding: 30px; border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,0.1);">
<h1 style="color: #333;">Добро пожаловать, {}!</h1>
<p style="color: #666; line-height: 1.6;">
Спасибо за регистрацию на нашем сервисе. Для завершения регистрации,
пожалуйста, подтвердите ваш email, перейдя по ссылке ниже:
</p>
<a href="{}"
style="display: inline-block; padding: 12px 24px; background: #007bff; color: white;
text-decoration: none; border-radius: 5px; margin: 20px 0;">
Подтвердить email
</a>
<p style="color: #999; font-size: 12px; margin-top: 30px;">
Если вы не регистрировались, просто проигнорируйте это письмо.
</p>
</div>
</body>
</html>"#,
data.username, data.confirmation_link
)
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Загружаем конфигурацию из переменных окружения
let smtp_host = env::var("SMTP_HOST").unwrap_or_else(|_| "smtp.gmail.com".to_string());
let smtp_username = env::var("SMTP_USERNAME")?;
let smtp_password = env::var("SMTP_PASSWORD")?;
let from_email = env::var("FROM_EMAIL")?;
// Подготавливаем данные письма
let email_data = EmailData {
to: "user@example.com".to_string(),
subject: "Подтверждение регистрации".to_string(),
username: "Иван Петров".to_string(),
confirmation_link: "https://example.com/confirm?token=abc123".to_string(),
};
// Создаём текстовую альтернативу
let text_body = format!(
"Добро пожаловать, {}!\n\nСпасибо за реги