Почему компилятор не позволяет мне использовать push на Vec, созданном с помощью collect ()? - PullRequest
0 голосов
/ 29 мая 2019

следующие компиляции:

pub fn build_proverb(list: &[&str]) -> String {
    if list.is_empty() {
        return String::new();
    }
    let mut result = (0..list.len() - 1)
        .map(|i| format!("For want of a {} the {} was lost.", list[i], list[i + 1]))
        .collect::<Vec<String>>();
    result.push(format!("And all for the want of a {}.", list[0]));
    result.join("\n")
}

Следующее не ( см. Детская площадка ):

pub fn build_proverb(list: &[&str]) -> String {
    if list.is_empty() {
        return String::new();
    }
    let mut result = (0..list.len() - 1)
        .map(|i| format!("For want of a {} the {} was lost.", list[i], list[i + 1]))
        .collect::<Vec<String>>()
        .push(format!("And all for the want of a {}.", list[0]))
        .join("\n");
    result
}

Компилятор говорит мне

error[E0599]: no method named `join` found for type `()` in the current scope
 --> src/lib.rs:9:10
  |
9 |         .join("\n");
  |          ^^^^

Я получаю ошибку того же типа, если пытаюсь написать только с push.

Что бы я ожидал, это то, что collect возвращает B, он же Vec<String>. Vec - это не (), и, конечно, Vec содержит методы, которые я хочу включить в список составных функций.

Почему я не могу составить эти функции? Объяснение может включать описание «магии» завершения выражения после collect(), чтобы заставить компилятор создавать экземпляр Vec так, как это не происходит, когда я сочиняю с push и т. Д.

Ответы [ 2 ]

5 голосов
/ 29 мая 2019

Если вы прочитаете документацию для Vec::push и посмотрите на подпись метода, вы узнаете, что он не возвращает Vec:

pub fn push(&mut self, value: T)

, так какявного типа возврата нет, тип возвращаемого значения - тип единицы ().Не существует метода с именем join на ().Вам нужно будет написать свой код в несколько строк.

См. Также:


Я бы написал это более функционально:

use itertools::Itertools; // 0.8.0

pub fn build_proverb(list: &[&str]) -> String {
    let last = list
        .get(0)
        .map(|d| format!("And all for the want of a {}.", d));

    list.windows(2)
        .map(|d| format!("For want of a {} the {} was lost.", d[0], d[1]))
        .chain(last)
        .join("\n")
}

fn main() {
    println!("{}", build_proverb(&["nail", "shoe"]));
}

См. Также:

0 голосов
/ 31 мая 2019

Спасибо всем за полезные взаимодействия.Все изложенное в предыдущем ответе точно правильно.И есть более широкая картина, когда я изучаю Rust.

Исходя из Haskell (с обучением C много лет назад), я наткнулся на подход цепочки методов OO, который использует указатель для цепочки между вызовами методов;нет необходимости в чистых функциях (то есть, что я делал с let mut result = ..., который затем использовался / требовался для изменения значения Vec с использованием push в result.push(...)).Я считаю, что более общее наблюдение заключается в том, что в ОО "aok" возвращено unit, потому что цепочка методов не требует возвращаемого значения.

Пользовательский код ниже определяет push как черту;он использует те же входные данные, что и «OO» push, но возвращает обновленный self.Возможно, только в качестве дополнительного комментария, это делает функцию чистой (вывод зависит от ввода), но на практике означает, что push, определенный как черта, позволяет составлять функции FP, которые я ожидал, была нормой (достаточно справедливо, я думалсначала учитывая, сколько Rust позаимствовал у Haskell).

То, что я пытался выполнить, и в основе вопроса заложено решение для кода, предложенное @Stargateur, @ E_net4 и @Shepmaster.Только самые маленькие правки следующие: (см. детская площадка )

pub fn build_proverb(list: &[&str]) -> String {
    if list.is_empty() {
        return String::new();
    }
    list.windows(2)
        .map(|d| format!("For want of a {} the {} was lost.", d[0], d[1]))
        .collect::<Vec<_>>()
        .push(format!("And all for the want of a {}.", list[0]))
        .join("\n")
}

Решение требует, чтобы я определил push как черту, которая возвращает self, тип Vec в этом случае.

trait MyPush<T> {
    fn push(self, x: T) -> Vec<T>;
}

impl<T> MyPush<T> for Vec<T> {
    fn push(mut self, x: T) -> Vec<T> {
        Vec::push(&mut self, x);
        self
    }
}

Последнее замечание: при рассмотрении многих признаков Rust я не смог найти функцию признака, которая возвращает () (по модулю, например, Write, которая возвращает Result ()).

Это контрастирует с тем, что я научился ожидать от struct и enum methods.Обе черты и методы OO имеют доступ к self и, таким образом, каждый из них описан как «методы», но, похоже, есть существенное отличие, на которое следует обратить внимание: методы OO используют ссылку, чтобы разрешить последовательное изменение self, черты FP (если хотите) использует композицию функций, которая полагается на использование «чистых», изменяющих состояние функций для достижения того же (:: (self, newValue) -> self).

Возможно, в качестве отступления, когда Haskell достигает ссылочной прозрачности в этой ситуации путем создания новой копии (по модулю закулисных оптимизаций), Rust, кажется, выполняет нечто подобное в пользовательском коде черты, управляя владением (передается вФункция trait, и возвращаемая, возвращая self).

Завершающий кусок головоломки «Функции составления»: чтобы композиция работала, вывод одной функции должен иметь тип, необходимый для вводаследующая функция.join работал как при передаче ему значения, так и при передаче ему ссылки (верно для типов, которые реализуют IntoIterator).Так что join, кажется, обладает способностью работать как в цепочках методов, так и в стилях композиции функций программирования.

Является ли это различие между ОО-методами, которые не полагаются на возвращаемое значение, и признаками, как правило, справедливыми в Rust?Вроде бы дело "здесь и там".Например, в отличие от push, где линия ясна, join, похоже, находится на пути к тому, чтобы стать частью стандартной библиотеки, определенной как метод для SliceConcatExt и функция черты для SliceConcatExt ( см. Руководство по ржавчине и обсуждение проблемы ржавчины ).Следующий вопрос, будет ли унификация подходов в стандартной библиотеке соответствовать философии дизайна Rust?(платите только за то, что вы используете, безопасно, эффективно, выразительно и приятно)

...