«Использование переменной типа из внешней функции» в замыкании, которое создает локальную переменную потока этого типа - PullRequest
0 голосов
/ 28 сентября 2018

Я пытаюсь создать пул потоков, где каждый поток в пуле имеет тип thread_local!, который может использоваться задачами в этом рабочем потоке.(T в приведенном ниже примере).Основное назначение этого класса состоит в том, что ресурс T не обязательно должен быть Send, поскольку он будет создаваться локально в каждом рабочем потоке с помощью фабричного метода, который равен Send.

Мой вариант использования - работа с пулом соединений !Send db, но я попытался сделать его универсальным для типа ресурса 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()
    }
}

( Playground )

К сожалению, я не могу получить свой код для проверки типов, когда я абстрагируюсь над T:

error[E0401]: can't use type parameters from outer function
  --> src/lib.rs:38:56
   |
17 | impl<T, M> RemoteResource<T, M>
   |      - type variable from outer function
...
28 |     pub fn call<F, R>(&mut self, f: F) -> R
   |            ---- try adding a local type parameter in this method instead
...
38 |                 static UNSENDABLE_TYPE: RefCell<Option<T>> = RefCell::new(None)
   |                                                        ^ use of type variable from outer function

Я попытался решить эту проблему, используя рекомендации из индекса ошибок компилятора Rust,но «копирование типа» не работает.Если «скопировать» T в call, то я получу ошибку «переменная затененного типа».Если я введу новый тип U, то я снова получу очень запутанный E401, но на этот раз он предлагает добавить параметр типа к параметрам типа в call, именно там, где я его уже добавил.Эта вторая вещь выглядит для меня как ошибка в компиляторе.(Если вам интересно об этом, вот детская площадка ).

Можно ли это проверить?Если нет, то почему?

1 Ответ

0 голосов
/ 28 сентября 2018

Это не имеет никакого отношения к закрытию и всему, что связано с static.Вот небольшой пример, который выдает ту же ошибку:

fn foo<T>() {
    static BAR: Option<T> = None;
}

A static в универсальной функции не будет генерировать одну статическую переменную для каждой T;есть только одна static переменная.Возьмите следующую программу, например:

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

fn main() {
    foo::<i32>();
    foo::<u64>();
}

Это напечатает:

1
2

Здесь foo::<i32> и foo::<u64> оба имеют один и тот же счетчик.Учитывая это, не имеет смысла определять единственную статику, тип которой зависит от параметра типа ее обобщающей функции, поскольку эту функцию можно создавать несколько раз.

К сожалению, нет способа определить"generic static", который создается для каждого используемого T.Вместо этого вы можете определить какую-то карту типов (например, карту от TypeId до Box<Any>) и выполнить динамический поиск на этой карте.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...