Rust异步资源自动释放库async-dropper-simple的使用,简化异步环境下的对象析构与资源管理

Rust异步资源自动释放库async-dropper-simple的使用,简化异步环境下的对象析构与资源管理

async-dropper-simple 是一个临时实现 AsyncDrop 功能的方案,提供了两种工作模式:

  1. async_dropper::simple - 基于StackOverflow上的实现方案
  2. async_dropper::derive - 提供AsyncDrop特性和派生宏,利用DefaultPartialEq来决定何时进行异步析构

特性标志

标志 描述
tokio 使用tokio异步运行时
async-std 使用async-std异步运行时
no-default-bound 通过将内部数据包装在Option<T>中避免对TDefault约束

安装方法

使用Cargo命令安装:

cargo add async-dropper-simple

或在Cargo.toml中添加:

async-dropper-simple = "0.2.6"

完整示例代码

use async_dropper_simple::{AsyncDrop, AsyncDropper};
use async_trait::async_trait;
use tokio::time::{sleep, Duration};

// 定义需要异步清理的资源结构体
struct AsyncResource {
    name: String,
    // 可能包含需要异步关闭的资源,如网络连接、文件句柄等
}

#[async_trait]
impl AsyncDrop for AsyncResource {
    async fn async_drop(&mut self) {
        // 模拟异步清理操作
        println!("Async dropping resource: {}", self.name);
        sleep(Duration::from_secs(1)).await;
        println!("Finished async dropping resource: {}", self.name);
    }
}

#[tokio::main]
async fn main() {
    {
        // 将资源包装在AsyncDropper中
        let resource = AsyncDropper::new(AsyncResource {
            name: "Test Resource".to_string(),
        });
        
        // 使用资源...
        println!("Using resource: {}", resource.inner().name);
        
        // 当resource离开作用域时,会自动执行async_drop
    } // <- 这里会触发异步析构
    
    // 等待足够时间让析构完成
    sleep(Duration::from_secs(2)).await;
}

示例解释

  1. 定义AsyncResource结构体表示需要异步清理的资源
  2. AsyncResource实现AsyncDrop特性,定义async_drop方法
  3. main函数中,将资源包装在AsyncDropper
  4. AsyncDropper离开作用域时自动调用async_drop方法
  5. 确保程序等待足够时间让异步清理完成

更完整的示例

下面是一个使用数据库连接的完整示例:

use async_dropper_simple::{AsyncDrop, AsyncDropper};
use async_trait::async_trait;
use tokio::time::{sleep, Duration};

// 模拟数据库连接结构体
struct DbConnection {
    url: String,
    connected: bool,
}

impl DbConnection {
    // 模拟连接数据库
    async fn connect(url: &str) -> Self {
        println!("Connecting to database: {}", url);
        sleep(Duration::from_millis(500)).await;
        println!("Successfully connected to database: {}", url);
        Self {
            url: url.to_string(),
            connected: true,
        }
    }

    // 模拟查询操作
    async fn query(&self, sql: &str) {
        println!("Executing query: {} on {}", sql, self.url);
        sleep(Duration::from_millis(200)).await;
        println!("Query completed");
    }
}

#[async_trait]
impl AsyncDrop for DbConnection {
    async fn async_drop(&mut self) {
        if self.connected {
            println!("Closing connection to: {}", self.url);
            sleep(Duration::from_millis(300)).await;
            self.connected = false;
            println!("Connection closed: {}", self.url);
        }
    }
}

#[tokio::main]
async fn main() {
    {
        // 创建并包装数据库连接
        let conn = AsyncDropper::new(
            DbConnection::connect("postgres://user:pass@localhost/db").await
        );
        
        // 使用连接执行查询
        conn.query("SELECT * FROM users").await;
        conn.query("UPDATE users SET name = 'test' WHERE id = 1").await;
    } // <- 自动关闭连接
    
    // 等待足够时间让清理完成
    sleep(Duration::from_secs(1)).await;
}

这个库特别适合需要异步清理资源的场景,如:

  • 数据库连接关闭
  • 网络连接断开
  • 文件IO操作完成后的清理
  • 任何需要异步操作的资源释放

1 回复

Rust异步资源自动释放库 async-dropper-simple 使用指南

async-dropper-simple 是一个简化异步环境下对象析构与资源管理的Rust库,它解决了在异步上下文中安全释放资源的问题。

简介

在同步Rust中,Drop trait 可以很好地管理资源释放,但在异步环境中,Drop 的实现不能包含异步代码。async-dropper-simple 提供了解决方案,允许你在异步环境中安全地释放资源。

安装

Cargo.toml 中添加依赖:

[dependencies]
async-dropper-simple = "0.1"

基本用法

1. 实现 AsyncDrop trait

use async_dropper_simple::{AsyncDrop, AsyncDropper};

struct AsyncResource {
    name: String,
    // 这里可以包含需要异步清理的资源
}

#[async_trait::async_trait]
impl AsyncDrop for AsyncResource {
    async fn async_drop(&mut self) {
        println!("Async dropping resource: {}", self.name);
        // 这里执行异步清理逻辑
        tokio::time::sleep(std::time::Duration::from_secs(1)).await;
        println!("Finished async drop for {}", self.name);
    }
}

2. 使用 AsyncDropper 包装

#[tokio::main]
async fn main() {
    let resource = AsyncDropper::new(AsyncResource {
        name: "test-resource".to_string(),
    });
    
    // 使用资源...
    println!("Using resource: {}", resource.inner().name);
    
    // 当 resource 离开作用域时,会自动调用 async_drop
}

高级用法

手动触发异步析构

#[tokio::main]
async fn main() {
    let mut resource = AsyncDropper::new(AsyncResource {
        name: "manual-drop".to_string(),
    });
    
    // 手动触发析构
    resource.async_drop().await;
    
    // 之后不能再使用该资源
}

禁用自动析构

#[tokio::main]
async fn main() {
    let resource = AsyncDropper::new(AsyncResource {
        name: "no-auto-drop".to_string(),
    });
    
    // 禁用自动析构
    let resource = resource.without_auto_drop();
    
    // 现在需要手动调用 async_drop
    // 否则资源不会被清理
}

实际示例:异步文件处理

use async_dropper_simple::{AsyncDrop, AsyncDropper};
use tokio::fs::File;
use tokio::io::AsyncWriteExt;

struct AsyncFile {
    file: Option<File>,
    path: String,
}

impl AsyncFile {
    async fn new(path: &str) -> std::io::Result<Self> {
        let file = File::create(path).await?;
        Ok(Self {
            file: Some(file),
            path: path.to_string(),
        })
    }
    
    async fn write(&mut self, content: &str) -> std::io::Result<()> {
        if let Some(file) = &mut self.file {
            file.write_all(content.as_bytes()).await?;
        }
        Ok(())
    }
}

#[async_trait::async_trait]
impl AsyncDrop for AsyncFile {
    async fn async_drop(&mut self) {
        println!("Closing file: {}", self.path);
        if let Some(file) = self.file.take() {
            // 在实际应用中,这里可能还需要刷新缓冲区等操作
            let _ = file.sync_all().await;
        }
    }
}

#[tokio::main]
async fn main() -> std::io::Result<()> {
    let mut file = AsyncDropper::new(AsyncFile::new("test.txt").await?);
    
    file.write("Hello, async drop!").await?;
    
    // 文件会在离开作用域时自动关闭
    Ok(())
}

完整示例:异步数据库连接管理

use async_dropper_simple::{AsyncDrop, AsyncDropper};
use tokio_postgres::{Client, NoTls};

struct AsyncDbConnection {
    client: Option<Client>,
    connection_string: String,
}

impl AsyncDbConnection {
    async fn new(conn_str: &str) -> Result<Self, tokio_postgres::Error> {
        let (client, connection) = tokio_postgres::connect(conn_str, NoTls).await?;
        
        // 启动连接任务
        tokio::spawn(async move {
            if let Err(e) = connection.await {
                eprintln!("Connection error: {}", e);
            }
        });
        
        Ok(Self {
            client: Some(client),
            connection_string: conn_str.to_string(),
        })
    }
    
    async fn query(&self, sql: &str) -> Result<Vec<tokio_postgres::Row>, tokio_postgres::Error> {
        if let Some(client) = &self.client {
            client.query(sql, &[]).await
        } else {
            Err(tokio_postgres::Error::connection_closed())
        }
    }
}

#[async_trait::async_trait]
impl AsyncDrop for AsyncDbConnection {
    async fn async_drop(&mut self) {
        println!("Closing database connection: {}", self.connection_string);
        if let Some(client) = self.client.take() {
            // 关闭连接
            let _ = client.close().await;
        }
    }
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let db = AsyncDropper::new(
        AsyncDbConnection::new("host=localhost user=postgres").await?
    );
    
    // 执行查询
    let rows = db.query("SELECT 1 + 1").await?;
    println!("Query result: {:?}", rows);
    
    // 连接会在离开作用域时自动关闭
    Ok(())
}

注意事项

  1. async-dropper-simple 需要与异步运行时(如 tokio、async-std)配合使用
  2. 确保 async_drop 方法不会panic,否则可能导致资源泄漏
  3. 对于需要同步和异步混合清理的资源,可以同时实现 DropAsyncDrop

这个库简化了异步环境下的资源管理,使得异步资源的生命周期管理更加直观和安全。

回到顶部