Почему компилятор Rust может определять типы в отдельных строках, но не в одной строке? - PullRequest
0 голосов
/ 05 мая 2020

Почему это разрешено:

let payload = "key1=value1";
let value: Vec<&str> = payload.split("=").collect();
let value = value[1];
println!("value is {:?}", value);

Но не это:

let payload = "key1=value1";
let value: Vec<&str> = payload.split("=").collect()[1];
println!("value is {:?}", value);

Это приводит к ошибке:

error[E0282]: type annotations needed
 --> src/main.rs:3:47
  |
3 |     let value: Vec<&str> = payload.split("=").collect()[1];
  |                                               ^^^^^^^
  |                                               |
  |                                               cannot infer type for type parameter `B` declared on the method `collect`
  |                                               help: consider specifying the type argument in the method call: `collect::<B>`
  |
  = note: type must be known at this point

Это не два эквивалент? Это одни и те же токены для анализа , с точки зрения компилятора, просто разделенные на разные строки.

Я не понимаю, какая дополнительная информация присутствует в первом синтаксисе, чтобы сделать вывод возможным .

Возможно, дело не в том, что вывод невозможен, как предупреждает компилятор, но это не разрешено из-за синтаксиса Rust, соглашения и т.д. c.?

Я пытаюсь понять почему не может сделать вывод компилятора (т.е. логически недостающая информация? только в соответствии с соглашением?), а не как исправить этот конкретный c случай.

Мышление об этом на уровне компилятора / парсера,

выражение : let value: Vec<&str> = payload.split("=").collect() суффикс : [1]

A (приемлемо):

(expression)
(expression')(suffix)

B (неприемлемо):

(expression)(suffix)

Учитывая, что expression' в основном добавляет value = value, что является тавтологией и не добавляет никакой новой информации,

Если expression и expression' имеют одинаковый объем информации, почему B недействителен для компилятора? Почему он не может сделать то же самое в B, если нет новой информации?

Я сомневаюсь в части «не могу сделать вывод» конкретно, а не в том, что «должно быть XX из-за синтаксиса / соглашения Rust YY»).

Это просто ограничение компилятора или преднамеренная ошибка для обеспечения удобочитаемости, или уникальная информация добавляется из одного случая в другой? (логически отличный сценарий ios).

Ответы [ 3 ]

3 голосов
/ 05 мая 2020

Первый фрагмент,

let value: Vec<&str> = payload.split("=").collect();
let value = value[1];

, говорит, что нужно собрать итератор в вектор (из &str s), а затем присвоить элемент с индексом 1 value (что будет быть &str). Обратите внимание, что первый value затенен вторым (они имеют разные типы, поэтому вам нужно сделать это, если вы хотите, чтобы они имели одинаковое имя).

Отчасти из-за аннотации типа, однострочная версия говорит о чем-то другом.

let value: Vec<&str> = payload.split("=").collect()[1];

Это говорит о том, что нужно собрать итератор в ... что-то ... и проиндексировать его в 1, а результирующий тип должен быть Vec<&str>. Теперь, поскольку любое количество типов потенциально может генерировать Vec<&str> при индексировании (это может быть Vec<Vec<&str>>, HashMap<usize, Vec<&str>> или даже какой-то тип, определенный в этом проекте или в зависимости), Rust не пытается здесь угадывать. Поскольку он не знает, в какой тип собирать, он отказывается и возвращает эту ошибку.

Чтобы сделать это правильно (в одной строке), вы, вероятно, захотите использовать синтаксис ol 'turbofi sh чтобы указать, какой тип вы хотите собрать.

let value = payload.split("=").collect::<Vec<_>>()[1];

(игровая площадка)

Обратите внимание, что тип элементов Vec известен поскольку это просто любой тип элементов итератора. Вот почему мы можем использовать Vec<_>, чтобы компилятор мог определить правильный тип.

Еще одна вещь. Чтобы быть более понятным c, вы можете использовать char вместо односимвольной строки в payload.split("="). payload.split'=') тоже подойдет.

0 голосов
/ 05 мая 2020

Аннотация вашего типа становится недействительной во втором примере. Что вы хотите:

let payload = "key1=value1";
let value: &str = payload.split("=").collect::<Vec<&str>>()[1];
println!("value is {:?}", value);

ссылка на ржавую площадку

Что можно упростить до этого:

let payload = "key1=value1";
let value = payload.split("=").collect::<Vec<_>>()[1];
println!("value is {:?}", value);

Причина, по которой вам нужно быть явным, потому что Iterator<Item = &str> можно собрать в дюжину различных типов коллекций, и компилятор не может угадать, какой из них вам нужен.

0 голосов
/ 05 мая 2020

Вы используете метод collect на итераторе, которому нужен «тип», чтобы знать, во что он должен собирать.

Кроме того, вам не нужно собирать, чтобы извлечь первый токен

let value = payload.split("=").nth(1).unwrap(); // you need to better error handling
println!("value is {:?}", value);

Если вы хотите использовать второй пример (неэффективный), вы можете добиться этого с помощью turbofi sh синтаксис

let value = payload.split("=").collect::<Vec<&str>>()[1];
println!("value is {:?}", value);

площадка

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