Как написать метод черты, принимая итератор строк, избегая мономорфизации (статическая диспетчеризация)?

Согласно https://docs.docker.com/engine/reference/builder/#run по умолчанию [Linux] для RUN является /bin/sh -c. Кажется, вы ожидаете багизмов, поэтому вы должны использовать «форму exec» для RUN, чтобы указать вашу оболочку.

RUN ["/bin/bash", "-c", "source /usr/local/bin/virtualenvwrapper.sh"]

В противном случае, используя «оболочечную форму» RUN и указав другую оболочку приводит к вложенным оболочкам.

# don't do this...
RUN /bin/bash -c "source /usr/local/bin/virtualenvwrapper.sh"
# because it is the same as this...
RUN ["/bin/sh", "-c", "/bin/bash" "-c" "source /usr/local/bin/virtualenvwrapper.sh"]

Если у вас более 1 команды, которым требуется другая оболочка, вы должны прочитать https://docs.docker.com/engine/reference/builder/ #shell и измените оболочку по умолчанию, поставив ее перед вашими командами RUN:

SHELL ["/bin/bash", "-c"]

Наконец, если вы поместили что-либо в файл .bashrc пользователя root, который вам нужен, вы можете добавьте флаг -l в команду SHELL или RUN, чтобы сделать его оболочкой входа и убедитесь, что она получена.

Примечание. Я намеренно игнорировал тот факт, что это бессмысленно для источника сценарий как единственная команда в RUN.

6
задан akavel 17 January 2019 в 00:40
поделиться

3 ответа

Я рекомендую прочитать этот вопрос , в котором содержится много полезной информации о том, почему вы не можете использовать дженерики с методами черты, если хотите использовать их в качестве объектов.

Короткий ответ: вы не можете делать то, что пытаетесь сделать: иметь функцию, которая принимает итератор любого типа (которая является связанной универсальной функцией), и при этом эта черта является объектобезопасной. [ 1118]

Есть несколько трюков, которые вы можете использовать , однако, это позволит вам манипулировать итераторами строк с помощью объекта-черты. Я рассмотрю каждый метод.

1. Используйте несколько методов в своей черте

В Rust есть только два типа строк: String и &str. Как вы указали в своем ответе, вы хотите работать с обоими. В этом случае все, что вам нужно сделать, это сделать два разных метода:

pub trait Store {
    fn query_valid_paths_str(&mut self, paths: &mut dyn Iterator<Item = &str>) -> Vec<String>;
    fn query_valid_paths_string(&mut self, paths: &mut dyn Iterator<Item = String>) -> Vec<String>;
}

Теперь, это становится нелогичным в определенный момент, если у вас слишком много типов, с которыми вы имеете дело. Но если есть только два, это самый простой вариант.

Если вы хотите использовать вместо этого IntoIterator, сигнатуры функций будут выглядеть следующим образом:

pub trait Store {
    fn query_valid_paths_str(&mut self, paths: &mut dyn IntoIterator<IntoIter = IntoIter<&str>, Item = &str>) -> Vec<String>;
    fn query_valid_paths_string(&mut self, paths: &mut dyn IntoIterator<IntoIter = IntoIter<String>, Item = String>) -> Vec<String>;
}

2. Используйте Box и динамическую диспетчеризацию

Этот подход гораздо более сложный и, вероятно, не стоит усилий, но я приведу его здесь в качестве доказательства концепции.

pub trait Store {
    fn query_valid_paths(&mut self, paths: &mut dyn Iterator<Item = &Box<dyn AsRef<str>>) -> Vec<String>;
}

Здесь paths - итератор над блоком, которому принадлежит объект черты AsRef<str>.

Это (насколько я знаю) единственный способ создать действительно полиморфное решение. Но какой ценой? Чтобы это работало, вам нужно не только явно объявить список, который вы передали как Vec<Box<AsRef<str>>>, это добавляет много накладных расходов при динамической диспетчеризации из указателей блока. Просто чтобы показать, насколько громоздким это может быть:

let mut str_vec: Vec<Box<AsRef<str>>> = vec!(Box::new("string one"), Box::new("string two".to_string()));
some_store_object.query_valid_paths(&mut str_vec.iter());

Я не рекомендую этот метод, если вам абсолютно не нужны эти функции. Вместо этого используйте первый метод.

Если вы используете этот метод, но хотите использовать его с IntoIterator, он будет выглядеть так:

pub trait Store {
    fn query_valid_paths(&mut self, paths: &mut dyn IntoIterator<IntoIter = IntoIter<Box<dyn AsRef<str>>>, Item = Box<dyn AsRef<str>>>) -> Vec<String>;
}
0
ответ дан ThatOneDeveloper 17 January 2019 в 00:40
поделиться

Помимо подхода Box, в определении черты можно определить общий тип. Этот метод ограничен реализуемым типом и использует статическую диспетчеризацию, но ваша черта не нарушит правила безопасности объекта.

trait Store<'a, I>
where
    I: IntoIterator<Item = &'a str>,
{
    fn query_valid_paths(&mut self, iter: I) -> Vec<String>;
}

impl<'a, I> Store<'a, I> for ()
where
    I: IntoIterator<Item = &'a str>,
{
    fn query_valid_paths(&mut self, iter: I) -> Vec<String> {
        iter.into_iter().map(|x| x.to_string()).collect()
    }
}

// Store is object safe
struct _Bar<'a, I> {
    vec: Vec<Box<dyn Store<'a, I>>>,
}

fn main() {
    let vec_of_strings = vec!["one", "two", "three"];
    println!("{:?}", ().query_valid_paths(vec_of_strings));
}

Чтобы не вступать во владение, вместо этого вы можете использовать vec_of_strings.iter().cloned().

0
ответ дан Caio 17 January 2019 в 00:40
поделиться

Я не думаю, что есть хорошее решение без статической отправки. Но документы для ошибки об объектах признаков с методами с общими параметрами фактически обеспечивают решение этой ситуации:

Во-первых, вы помечаете свой метод как where Self: Sized - это делает его недоступным черты объектов. Может быть, вам не нужен этот метод в контексте объекта trait - тогда все готово.

Если вам нужен метод в контексте объекта признака, вы можете снова использовать его с помощью типоразмера, который содержит объект признака, например, Box:

struct MyStruct(i32);

pub trait Store {
    fn len_of_first_str(&self, paths: impl IntoIterator<Item = impl AsRef<str>>) -> usize
    where Self: Sized{
        paths.into_iter().next().unwrap().as_ref().len()
    }
}

impl Store for Box<dyn Store>{}

fn myfun(arg: Box<dyn Store>) -> usize {
    arg.len_of_first_str(vec!["string"])
}
0
ответ дан Chronial 17 January 2019 в 00:40
поделиться
Другие вопросы по тегам:

Похожие вопросы: