lettre для Rust: полное руководство с примерами

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Библиотека 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Спасибо за реги                    

Рекомендаций