Согласно 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.
Я рекомендую прочитать этот вопрос , в котором содержится много полезной информации о том, почему вы не можете использовать дженерики с методами черты, если хотите использовать их в качестве объектов.
Короткий ответ: вы не можете делать то, что пытаетесь сделать: иметь функцию, которая принимает итератор любого типа (которая является связанной универсальной функцией), и при этом эта черта является объектобезопасной. [ 1118]
Есть несколько трюков, которые вы можете использовать , однако, это позволит вам манипулировать итераторами строк с помощью объекта-черты. Я рассмотрю каждый метод.
В 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>;
}
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>;
}
Помимо подхода 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()
.
Я не думаю, что есть хорошее решение без статической отправки. Но документы для ошибки об объектах признаков с методами с общими параметрами фактически обеспечивают решение этой ситуации:
Во-первых, вы помечаете свой метод как 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"])
}