Использование Into <String>на интерфейсах - нулевая стоимость для Vec? - PullRequest
0 голосов
/ 02 декабря 2018

Я использую Into<String> в качестве типа параметра, чтобы разрешить создание Person с, например, str, а также принадлежащим String

pub struct Person {
    pub first_name: String,
    pub last_name: String,
}

impl Person {
    pub fn new<S>(first_name: S, last_name: S) -> Self
    where S: Into<String>
    {
        Self {
           first_name: first_name.into(),
           last_name: last_name.into(),
        }
    }
}

Насколько я знаю, из-замономорфизму, это нулевая стоимость во время выполнения.

Теперь, я уверен, это не нулевая стоимость:

pub struct Person {
    pub first_name: String,
    pub middle_names: Vec<String>,
    pub last_name: String,
}

impl Person {
    pub fn new<S>(first_name: S, middle_names: Vec<S>, last_name: S) -> Self
    where S: Into<String>
    {
        let middle_names: Vec<String> = middle_names.into_iter().map(|s| s.into()).collect();

        Self {
            first_name: first_name.into(),
            middle_names,
            last_name: last_name.into(),
        }
    }
}

Интересно, смогу ли я как-то сделать это "нулевой стоимостью"».Vec означает, что данные находятся в куче, но, возможно, я могу заменить Vec какой-либо другой полезной структурой данных или даже срезом.

Есть идеи?

1 Ответ

0 голосов
/ 02 декабря 2018

Вот несколько решений, которые я могу придумать.

Реализация двух разных конструкторов

Вы можете реализовать два отдельных конструктора - один для S = String и один для S: Into<String>.Людям придется использовать разные имена для двух случаев, если они заботятся об оптимизации.

Игнорировать проблему

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

Используйте специализацию

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

#![feature(specialization)]

trait IntoStringVec: Sized {
    fn into_string_vec(v: Vec<Self>) -> Vec<String>;
}

impl IntoStringVec for String {
    fn into_string_vec(v: Vec<String>) -> Vec<String> {
        v
    }
}

impl<T: Into<String>> IntoStringVec for T {
    default fn into_string_vec(v: Vec<Self>) -> Vec<String> {
        v.into_iter().map(Into::into).collect()
    }
}

Это решение требует использования Rust nightly.

Использование небезопасного кода для несанкционированного доступа.преобразование места

Можно выполнить преобразование вектора на месте для случая size_of::<S>() == size_of::<String>() и align_of::<S>() >= align_of::<String>().Делать это вручную немного сложно, поэтому я рекомендую найти какой-нибудь ящик, который реализует это, например map_in_place crate .


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

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