Как изменить мой конструктор так, чтобы он принимал либо фрагмент, либо ссылку на массив или вектор - PullRequest
0 голосов
/ 11 февраля 2019

Это упрощенный пример моего кода:

#[derive(Debug, Clone, Copy)]
enum Data<'a> {
    I32(&'a [i32]),
    F64(&'a [f64]),
}

impl<'a> From<&'a [i32]> for Data<'a> {
    fn from(v: &'a [i32]) -> Data<'a> {
        Data::I32(v)
    }
}

impl<'a> From<&'a [f64]> for Data<'a> {
    fn from(v: &'a [f64]) -> Data<'a> {
        Data::F64(v)
    }
}

#[derive(Debug, Clone, Copy)]
struct DataVar<'a> {
    name: &'a str,
    data: Data<'a>,
}

impl<'a> DataVar<'a> {
    fn new<T>(name: &'a str, data: T) -> Self
    where
        T: Into<Data<'a>>,
    {
        Self {
            name,
            data: data.into(),
        }
    }
}

Прежде всего, учитывая, что мне нужно привести разные DataVar s к одному и тому же вектору, и я хотел бы избежать использования объектов-чертКак вы думаете, моя реализация верна или у вас есть предложения по улучшению?

Теперь мой главный вопрос.Я могу определить новые DataVar s, передающие срез, например, следующим образом:

let x = [1, 2, 3];
let xvar = DataVar::new("x", &x[..]);

Как я могу изменить свой конструктор, чтобы он работал не только со срезом, но также со ссылкой на массив иливектор?Например, я хотел бы, чтобы следующее работало также:

let x = [1, 2, 3];
let xvar = DataVar::new("x", &x);

РЕДАКТИРОВАТЬ:

Теперь я попытался реализовать тот же код, используя объект признака вместо перечисления, но результат дажехуже ... разве нет никакого решения для этого?

trait Data: std::fmt::Debug {}

impl Data for &[i32] {}

impl Data for &[f64] {}

#[derive(Debug, Clone, Copy)]
struct DataVar<'a> {
    name: &'a str,
    data: &'a dyn Data,
}

impl<'a> DataVar<'a> {
    fn new<T>(name: &'a str, data: &'a T) -> Self
    where
        T: Data,
    {
        Self { name, data }
    }
}

let x = [1, 2, 3];
let xvar = DataVar::new("x", &&x[..]);

Ответы [ 5 ]

0 голосов
/ 15 февраля 2019

На самом деле, это ИМХО лучшее решение ... так что, как и в моем исходном коде, мне просто нужно было немного исправить в конструкторе new:

#[derive(Debug, Clone, Copy)]
enum Data<'a> {
    I32(&'a [i32]),
    F64(&'a [f64]),
}

impl<'a> From<&'a [i32]> for Data<'a> {
    fn from(data: &'a [i32]) -> Data<'a> {
        Data::I32(data)
    }
}

impl<'a> From<&'a [f64]> for Data<'a> {
    fn from(data: &'a [f64]) -> Data<'a> {
        Data::F64(data)
    }
}

#[derive(Debug, Clone, Copy)]
struct DataVar<'a> {
    name: &'a str,
    data: Data<'a>,
}

impl<'a> DataVar<'a> {
    fn new<T>(name: &'a str, data: &'a [T]) -> Self
    where
        &'a [T]: Into<Data<'a>>,
    {
        Self {
            name,
            data: data.into(),
        }
    }
}
0 голосов
/ 15 февраля 2019

@ trentcl ваше решение великолепно!Теперь я вижу, как использовать принуждение.

Однако я немного подправил его следующим образом, я, наконец, буду использовать этот код, если вы не увидите в нем никаких недостатков, спасибо!

#[derive(Debug, Clone, Copy)]
enum Data<'a> {
    I32(&'a [i32]),
    F64(&'a [f64]),
}

trait IntoData<'a>: Sized {
    fn into_data(&self) -> Data<'a>;
}

impl<'a> IntoData<'a> for &'a [i32] {
    fn into_data(&self) -> Data<'a> {
        Data::I32(&self)
    }
}

impl<'a> IntoData<'a> for &'a [f64] {
    fn into_data(&self) -> Data<'a> {
        Data::F64(&self)
    }
}

#[derive(Debug, Clone, Copy)]
struct DataVar<'a> {
    name: &'a str,
    data: Data<'a>,
}

impl<'a> DataVar<'a> {
    fn new<T>(name: &'a str, data: &'a [T]) -> Self
    where
        &'a [T]: IntoData<'a>,
    {
        Self {
            name,
            data: data.into_data(),
        }
    }
}
0 голосов
/ 13 февраля 2019

На самом деле это лучшее решение для моего случая:

impl<'a> DataVar<'a> {
    fn new<T, U>(name: &'a str, data: &'a T) -> Self
    where
        T: AsRef<[U]> + ?Sized,
        U: 'a,
        &'a [U]: Into<Data<'a>>,
    {
        Self {
            name,
            data: data.as_ref().into(),
        }
    }
}

Работает со срезами, ссылками на векторы и ссылками на массивы длиной до 32, которые реализуют AsRef<[T]> https://doc.rust-lang.org/beta/std/convert/trait.AsRef.html

Спасибо @Francis за ваши советы!

0 голосов
/ 14 февраля 2019

Мне кажется, что AsRef не является правильной абстракцией по двум причинам: во-первых, потому что для типа возможно (хотя и маловероятно) реализовать оба типа AsRef<[i32]> и AsRef<[f64]>, и непонятно, чтодолжно произойти в этом случае;и во-вторых, потому что уже есть встроенная языковая функция ( принуждение ), которая может превратить Vec<T> или &[T; n] в &[T], и вы этим не пользуетесь.

Я хотел бы написать функцию new, которая выглядит в основном так:

    fn new<T>(name: &'a str, data: &'a [T]) -> Self
    where
        // what goes here?

Она автоматически будет работать с &[T; n], &Vec<T>, &Cow<T> и т. Д., Еслимы можем сказать компилятору, что делать с T.Имеет смысл, что вы могли бы создать черту, которая знает, как преобразовать &'a [Self] в Data и реализована для i32 и f64, поэтому давайте сделаем это:

trait Item: Sized {
    fn into_data<'a>(v: &'a [Self]) -> Data<'a>;
}

impl Item for i32 {
    fn into_data<'a>(v: &'a [i32]) -> Data<'a> {
        Data::I32(v)
    }
}

impl Item for f64 {
    fn into_data<'a>(v: &'a [f64]) -> Data<'a> {
        Data::F64(v)
    }
}

Граница привязкина new становится тривиально:

impl<'a> DataVar<'a> {
    fn new<T>(name: &'a str, data: &'a [T]) -> Self
    where
        T: Item,
    {
        Self {
            name,
            data: T::into_data(data),
        }
    }
}

Я считаю, что это более читабельно, чем версия с From и AsRef, но если вы все еще хотите From, вы можете легко добавить его с помощью универсальногоimpl:

impl<'a, T> From<&'a [T]> for Data<'a>
where
    T: Item,
{
    fn from(v: &'a [T]) -> Self {
        T::into_data(v)
    }
}
0 голосов
/ 11 февраля 2019

Мы можем использовать черту AsRef для преобразования ссылок на массивы или векторы в срезы.AsRef - это общая характеристика, поэтому нам нужно ввести параметр второго типа для представления «промежуточного типа» (типа среза).После вызова as_ref у нас есть срез, который можно преобразовать в Data с помощью into.

impl<'a> DataVar<'a> {
    fn new<T, U>(name: &'a str, data: &'a T) -> Self
    where
        T: AsRef<U> + ?Sized,
        U: ?Sized + 'a,
        &'a U: Into<Data<'a>>,
    {
        Self {
            name,
            data: data.as_ref().into(),
        }
    }
}

Обратите внимание, что параметр data теперь является ссылкой: этонеобходимо, поскольку время жизни ссылки, возвращаемой as_ref, ограничено временем жизни параметра self, переданного в as_ref.Если мы изменили параметр обратно на data: T, то data.as_ref() теперь неявно ссылается на data для вызова as_ref, что предполагает общую ссылку на self (&self).Но data здесь является локальным параметром, что означает, что время жизни ссылки, созданной этой неявной операцией ссылки, ограничено локальной функцией, и поэтому ссылка, возвращаемая data.as_ref().Это время жизни короче 'a, поэтому мы не можем сохранить его в DataVar и вернуть его.

Если вам нужно обработать data значения, которые не являются ссылками, в дополнение к значениям, которыессылки, это решение не может поддержать это, к сожалению.

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