在创建该类型的线程局部变量的闭包中“使用来自外部函数的类型变量”

mas*_*onk 1 rust

我正在尝试构建一个线程池,其中池中的每个线程都有一个thread_local! 可供该工作线程上的任务使用的类型。(T在下面的例子中)。该类的主要目的是资源T不需要Send,因为它将通过工厂方法在每个工作线程上本地构造, Send.

我的用例是在一个!Send数据库连接池上工作,但我试图使它在资源类型上通用T

extern crate threadpool;

use std::sync::mpsc::channel;
use std::sync::Arc;

// A RemoteResource is a threadpool that maintains a threadlocal ?Send resource
// on every pool in the thread, which tasks sent to the pool can reference.
// It can be used e.g., to manage a pool of database connections.
struct RemoteResource<T, M>
where
    M: 'static + Send + Sync + Fn() -> T,
{
    pool: threadpool::ThreadPool,
    make_resource: Arc<M>,
}

impl<T, M> RemoteResource<T, M>
where
    M: Send + Sync + Fn() -> T,
{
    pub fn new(num_workers: usize, make_resource: M) -> Self {
        RemoteResource {
            pool: threadpool::ThreadPool::new(num_workers),
            make_resource: Arc::new(make_resource),
        }
    }

    pub fn call<F, R>(&mut self, f: F) -> R
    where
        R: 'static + Send,
        F: 'static + ::std::marker::Send + FnOnce(&mut T) -> R,
    {
        let (tx, rx) = channel();
        let maker = self.make_resource.clone();
        self.pool.execute(move || {
            use std::cell::RefCell;
            thread_local!{
                static UNSENDABLE_TYPE: RefCell<Option<T>> = RefCell::new(None)
            }
            UNSENDABLE_TYPE.with(|it| {
                let mut mine = it.borrow_mut();
                if mine.is_none() {
                    *mine = Some(maker());
                }
                if let Some(ref mut mine) = *mine {
                    let res = f(mine);
                    tx.send(res).unwrap();
                    return ();
                }
                unreachable!()
            });
        });
        rx.recv().unwrap()
    }
}
Run Code Online (Sandbox Code Playgroud)

游乐场

不幸的是,当我抽象时,我无法让我的代码进行类型检查T

extern crate threadpool;

use std::sync::mpsc::channel;
use std::sync::Arc;

// A RemoteResource is a threadpool that maintains a threadlocal ?Send resource
// on every pool in the thread, which tasks sent to the pool can reference.
// It can be used e.g., to manage a pool of database connections.
struct RemoteResource<T, M>
where
    M: 'static + Send + Sync + Fn() -> T,
{
    pool: threadpool::ThreadPool,
    make_resource: Arc<M>,
}

impl<T, M> RemoteResource<T, M>
where
    M: Send + Sync + Fn() -> T,
{
    pub fn new(num_workers: usize, make_resource: M) -> Self {
        RemoteResource {
            pool: threadpool::ThreadPool::new(num_workers),
            make_resource: Arc::new(make_resource),
        }
    }

    pub fn call<F, R>(&mut self, f: F) -> R
    where
        R: 'static + Send,
        F: 'static + ::std::marker::Send + FnOnce(&mut T) -> R,
    {
        let (tx, rx) = channel();
        let maker = self.make_resource.clone();
        self.pool.execute(move || {
            use std::cell::RefCell;
            thread_local!{
                static UNSENDABLE_TYPE: RefCell<Option<T>> = RefCell::new(None)
            }
            UNSENDABLE_TYPE.with(|it| {
                let mut mine = it.borrow_mut();
                if mine.is_none() {
                    *mine = Some(maker());
                }
                if let Some(ref mut mine) = *mine {
                    let res = f(mine);
                    tx.send(res).unwrap();
                    return ();
                }
                unreachable!()
            });
        });
        rx.recv().unwrap()
    }
}
Run Code Online (Sandbox Code Playgroud)

我尝试使用 Rust 编译器错误索引中的建议解决此问题,但“复制类型”不起作用。如果“复制”Tcall,那么我会收到“阴影类型变量”错误。如果我引入一个新类型U,那么我再次得到一个非常令人困惑的 E401,但这次建议我尝试向 上的类型参数添加一个类型参数,这call正是我实际已经添加的位置。第二件事对我来说看起来像是编译器中的一个错误。(如果你对此感到好奇,这里是游乐场)。

是否有可能让它进行类型检查?如果没有,为什么不呢?

Fra*_*gné 6

它与闭包无关,与static. 这是一个产生相同错误的较小示例:

fn foo<T>() {
    static BAR: Option<T> = None;
}
Run Code Online (Sandbox Code Playgroud)

static泛型函数中的A不会为每个生成一个静态T;只有一个static变量。以下面的程序为例:

fn foo<T>() {
    static mut COUNTER: i32 = 0;
    unsafe {
        COUNTER += 1;
        println!("{}", COUNTER);
    }
}

fn main() {
    foo::<i32>();
    foo::<u64>();
}
Run Code Online (Sandbox Code Playgroud)

这打印:

1
2
Run Code Online (Sandbox Code Playgroud)

在这里,foo::<i32>foo::<u64>共享同一个柜台。考虑到这一点,定义一个静态的类型取决于其封闭泛型函数的类型参数是没有意义的,因为该函数可以被多次实例化。

不幸的是,没有办法定义static为每个T使用的实例化的“通用” 。您可以做的是定义某种类型映射(即从TypeIdto映射Box<Any>)并在该映射中执行动态查找。