Rust — Могу ли я сделать эту функцию diesel dsl::find() более универсальной?

#database #generics #rust #rust-diesel

#База данных #общие #Ржавчина #rust-дизель

Вопрос:

У меня есть функция, которая использует diesel для получения объекта из базы данных на основе заданного идентификатора:

 fn get_db_site(pool: web::Data<Pool>, site_id: u32) -> Result<Site, diesel::result::Error> {
    let conn = pool.get().unwrap();                                                           
    dsl::site.find(site_id).get_result::<Site>(amp;conn)                                         
}                                                                                           
  

Эта функция будет точно такой же для каждой таблицы, в которой я хочу ее запустить, поэтому я надеюсь поместить ее в собственный файл utils, чтобы мне не приходилось вводить одно и то же каждый раз. Единственная проблема заключается в том, что для вызова этой находки мне нужно сделать
crate::schema::site::dsl::site.find и я не уверен, как я могу сделать этот вызов универсальным для любого типа. Я знаю, что есть аргументы типа, но я не думаю, что это сработает здесь

Ответ №1:

Обычно я не советую делать вещи diesel более универсальными, поскольку это довольно быстро приводит к действительно сложным границам признаков. Обычно вы никогда не захотите делать это в коде приложения. (Это другое дело для библиотек, которые должны быть универсальными). Обычно я сравниваю ситуацию с обычным SQL. Например, если кто-то жалуется, что это users::table.find(pk) похоже на дублирование, задайте себе следующий вопрос: считаете ли вы, что это SELECT … FROM users дублируется в соответствующем запросе SELECT … FROM users WHERE id = $ . (Оператор diesel dsl в основном тот же).

Итак, чтобы ответить на ваш актуальный вопрос, общие функции должны выглядеть примерно так: (Не уверен, правильно ли я определил все границы без тестирования)

 

fn get_db_thing<T, U, PK>(pool: web::Data<Pool>, primary_key: PK) -> Result<U, diesel::result::Error> 
where T: Table   HasTable<Table = T>,
      T: FindDsl<PK>,
      U: Queryable<SqlTypeOf<Find<T, PK>>, Pg>
{
    let conn = pool.get().unwrap();                                                           
    T::table().find(primary_key).get_result::<U>(amp;conn)                                         
}     
  

Как вы можете видеть, список границ признаков уже намного длиннее, чем просто загрузка, встроенная в соответствующие функции. Кроме того, все детали, добавленные при построении запроса, теперь будут требоваться в качестве аргумента универсальной функции. По крайней мере, тип для T не может быть определен компилятором, поэтому с точки зрения размера кода это решение не «проще», чем просто не делать его универсальным.