LINUX.ORG.RU

Функция, принимающая любую последовательность?

 , ,


2

6

Пытаюсь на расте написать аналог такого:

def print_items(items):
    for item in items:
        print item

Это же просто, да?

Растишкин код:

// так работает
fn print_items(items: &[&str]) {
    for item in items {
        println!("{}", item);
    }
}

// а так не работает, блджад!
fn print_items_iter<'a, I> (items: I)
    where I: IntoIterator<Item = &'a str>
{
    for item in items {
        println!("{}", item);
    }
}

fn main() {
    let items = ["pracuj", "kurwo"];
    print_items(&items);
    print_items_iter(&items);
}

Не компилится:

src/main.rs:21:5: 21:21 error: type mismatch resolving `<&[&str; 2] as core::iter::IntoIterator>::Item == &str`:
 expected &-ptr,
    found str [E0271]
src/main.rs:21     print_items_iter(&items);
                   ^~~~~~~~~~~~~~~~
src/main.rs:21:5: 21:21 help: run `rustc --explain E0271` to see a detailed explanation
src/main.rs:21:5: 21:21 note: required by `print_items_iter`

Штоааа? В доке же написано, что слайсы реализуют IntoIterator. Чего он от меня хочет? Алсо, &[&str; 2] - это таки слайс или ссылка на массив? Почему? Я уже совсем ничего нипа

Растишка: формула здорового росту.



Последнее исправление: HeipaVai1o (всего исправлений: 2)
Ответ на: комментарий от den73

если что-то много раз повторяется

Такие списки трейтов много раз не повторяются, а если и повторяются, то случайно совпали, а не взаимосвязаны.

anonymous
()
Ответ на: комментарий от intelfx

в конкретно той функции, что я выше написал, возможен только static dispatch

Ну да, в IntoIterator получится шаблонный возвращаемый тип. Но можно запилить dynamic dispatch на чистых итераторах. Правда, вызов получается... своеобразным:

http://is.gd/nJt0QT

anonymous
()
Ответ на: комментарий от DarkEld3r

Ну а так мы получим на одно действие больше, а профит сомнителен.

Это касается и функций, и структур.

Впрочем, основной аргумент у меня был другим, а за «чтение слева направо» ты ратовал.

Это другое. Чтение слева направо не противоречит необходимости куда-то слазить. Получается чтение слева-направо со стеком. А в С чтение непойми откуда и непойми куда.

Ну в общем ладно, это всё уже священные войны. Всем спасибо за ответы, теперь я немного больше знаю про раст.

den73 ★★★★★
()
Последнее исправление: den73 (всего исправлений: 1)

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

fn choose<'a, 'b>(items: &[&'b str]) -> &'b str {
    items.into_iter().next().unwrap()
}

fn choose_iter<'a, 'b, I>(items: I) -> &'b str
    where I: IntoIterator<Item = &'b &'b str>
{
    items.into_iter().next().unwrap()
}

fn main() {
    let banana = "банановый";

    // так работает
    let result = choose(&[banana]);
    println!("А теперь {}!", result);

    // так хочет странного
    let result = choose_iter(&[banana]);
    println!("А теперь {}!", result);
}

Первый вариант работает, второй тупит:

src/main.rs:19:31: 19:39 error: borrowed value does not live long enough
src/main.rs:19     let result = choose_iter(&[banana]);
                                             ^~~~~~~~
src/main.rs:19:41: 21:2 note: reference must be valid for the block suffix following statement 3 at 19:40...
src/main.rs:19     let result = choose_iter(&[banana]);
src/main.rs:20     println!("А теперь {}!", result);
src/main.rs:21 }
src/main.rs:19:5: 19:41 note: ...but borrowed value is only valid for the statement at 19:4
src/main.rs:19     let result = choose_iter(&[banana]);
                   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/main.rs:19:5: 19:41 help: consider using a `let` binding to increase its lifetime
src/main.rs:19     let result = choose_iter(&[banana]);
                   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

С чего он решил, что массив ссылок должен жить до конца блока? Как ему обосновать, что он не прав? Ссылка на playground, кому интересно.

HeipaVai1o
() автор топика
Ответ на: комментарий от HeipaVai1o
help: consider using a `let` binding to increase its lifetime
let slice = [banana];
let result = choose_iter(&slice);
anonymous
()
Ответ на: комментарий от HeipaVai1o

Всё правильно. choose_iter возвращает ссылку не на строку, а на элемент переданного массива (содержащий ссылку на строку). А так как переданный массив временный, то он сразу уничтожается. Так работает: http://is.gd/VIVXWU

red75prim ★★★
()
Ответ на: комментарий от HeipaVai1o

С чего он решил, что массив ссылок должен жить до конца блока?

В первом случае в момент вызова создаётся временный слайс и передаётся ссылка на него. Компилятор может увидеть, что внутренности слайса живут столько же, сколько результат.

Во втором случае, мы передаём итератор по значению и временный слайс умрёт раньше, чем мы хотели бы.

И зачем тебе лайфтайм 'a?

DarkEld3r ★★★★★
()
Ответ на: комментарий от red75prim

Всё правильно. choose_iter возвращает ссылку не на строку, а на элемент переданного массива (содержащий ссылку на строку).

Это как так? Тип возвращаемого значения &str, а не &&str, поэтому должна возвращаться ссылка на строку. Или почему?

HeipaVai1o
() автор топика
Ответ на: комментарий от DarkEld3r

Во втором случае, мы передаём итератор по значению и временный слайс умрёт раньше, чем мы хотели бы.

По какому же значению, если передаётся тот же самый слайс? И передаётся даже не итератор, а нечто, имеющее метод into_iter().

HeipaVai1o
() автор топика
Ответ на: комментарий от HeipaVai1o
// IntoIterator for slice
impl<'a, T> IntoIterator for &'a [T]

// Iterator over slice
impl<'a, T> Iterator for Iter<'a, T>
type Item = &'a T

// slice = [T]
// Item = &T

// slice = [&str]
// Item = &&str
anonymous
()
Ответ на: комментарий от anonymous

И чо? Можно вообще так написать, и будет работать:

&&&&&&&&&&&&items.into_iter().next().unwrap()

Тип возвращаемого значения &str, поэтому &&str автоматически разыменовывается в &str.

HeipaVai1o
() автор топика
Ответ на: комментарий от HeipaVai1o

Всё так плохо...

  • IntoIterator реализуется для ссылки на срез.
  • когда ты в функцию передаёшь этот срез, с ним же идёт время жизни.
  • это время жизни равно времени жизни его элементов.
  • но т.к. срез создаёшь в вызове функции, его времени недостаточно, чтобы результат поместить в переменную.
  • а всё потому, что Item = ссылка на элемент.
anonymous
()
Ответ на: комментарий от anonymous

Что-то не улавливаю твоей логики. Срез существует до конца let-стейтмента, в котором вызывается функция. Этого как раз достаточно, чтобы результат поместить в переменную.

Алсо, в первом варианте в функцию передаётся точно такой же срез и используется тот же самый метод into_iter() из трейта IntoIterator. Код идентичный, разница только в типе функции.

а всё потому, что Item = ссылка на элемент.

ОК, и как из этого следует, что слайс, передаваемый в функцию, должен жить до конца блока? Ссылку на элемент никто из функции возвращает, возвращается сам элемент последовательности.

HeipaVai1o
() автор топика
Ответ на: комментарий от HeipaVai1o

Потому что написано Item = &'b &'b str , то есть временный слайс должен жить столько же сколько и возвращаемое значение.

Так работает:

fn choose_iter<'a, 'b: 'a, I>(items: I) -> &'b str
    where I: IntoIterator<Item = &'a &'b str>
{
    items.into_iter().next().unwrap()
}

Вроде бы теперь и сам понял.

red75prim ★★★
()
Ответ на: комментарий от HeipaVai1o

По какому же значению, если передаётся тот же самый слайс?

Правильнее, конечно, будет сказать, что не «передаётся по значению», а «перемещается». Ну а в первом случае всё-таки передача по ссылке.

DarkEld3r ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.