Строки, функции и ящики / модули

В нашей серии dna_toolkit мы широко использовали строки, списки и словари Python. Эти структуры данных очень полезны и делают нашу жизнь очень простой. Посмотрим, что может предложить Rust.

Начнем с Strings. Ржавчина бывает двух типов. Один называется «Строка», а другой больше похож на строку в других языках, то есть просто «Строка». Я собираюсь дать ссылку на потрясающее видео о строках в Rust в разделе «Ссылки» ниже. Обязательно посмотрите его, если вы новичок в этой части Rust.

В этой статье мы сосредоточимся на рассмотрении того, как мы можем использовать строки в Rust для репликации некоторых из нашего кода «dna_toolkit». В предыдущей статье мы создали два файла для Rust и Python: «main.rs» и «main.py» соответственно. Мы добавили весь код в эти два файла.

Чтобы избежать добавления большого количества кода в один файл, давайте создадим отдельные файлы для каждого видео, превратим код в функции и включим / импортируем их в наши «основные» файлы.

Итак, вот структура проекта, которую мы создали в нашей первой статье:

Создадим два новых файла; intro.py и intro.rs, переместите весь код из наших основных файлов в эти файлы и включите / импортировать их. Итак, теперь наш проект должен выглядеть так:

Теперь, когда мы создали структуру, которую будем использовать, давайте добавим еще два файла под названием «строки» и перейдем к нашему первому сегменту.

Часть 1: Струны

Итак, теперь у нас должно получиться что-то вроде этого:

Обратите внимание, что мы добавили символ подчеркивания в наши функции Rust: _intro (), _strings (). Это сделано, чтобы избежать появления этого предупреждающего сообщения компилятора:

= note: `#[warn(dead_code)]` on by default

Мы закомментируем вызовы предыдущих функций, чтобы избежать одновременного вывода большого количества результатов, поэтому мы можем сосредоточиться только на выводе кода текущей статьи. В этом случае Rust сообщает нам, что существует «мертвый код», что означает, что у нас есть функция _intro (), которая включена, но никогда не вызывается. Это не проблема, просто не забудьте добавить подчеркивание ко всем функциям Rust, которые вы не используете / не вызываете.

Мы сосредоточимся на String, поскольку это структура данных, размещенная в куче, то есть мы можем изменять ее во время выполнения, и она имеет набор очень полезных методов.

Python не заботится о том, добавляете ли вы «символ» или другую «строку» к нашей основной строке, но Rust строго к этому относится. Здесь у нас есть два метода Rust для:

  • push_str (««); - добавляет строку. Требуются двойные кавычки ““. Строка 4.
  • push (‘‘); - добавляет один символ. Требуются одинарные кавычки . Строка 7.

Если в Python мы просто говорим test_str = "Doom" и Python понимает, что это строка, в Rust нам нужно явно указать компилятору: let mut test_str = String::from("Doom");

Вы также можете создать пустую строку следующим образом:
let mut test_str = String::new();

Оба кода выведут это:

Ознакомьтесь с некоторыми другими методами, которые есть в Rust String: https://www.tutorialspoint.com/rust/rust_string.htm

Давайте объединим две существующие строки:

В Python это просто. Это просто работает, и мы можем продолжить доступ ко всем трем переменным после того, как запустим этот код.

Если мы попытаемся сделать то же самое в Rust, мы столкнемся с проблемой. Давайте взглянем:

Обратите внимание, что когда мы добавляем строки, первая строка не имеет символа амперсанда &. Каждая строка, которую мы добавляем после этого, должна содержать символ &.

Если мы запустим это, мы увидим эту проблему:

println!("{} {} {}", p1, p2, p3);
                   | ^^ value borrowed here after move

В строке 4 мы передали p3 право собственности на p1 и снова пытаемся получить к нему доступ в строке 5, распечатав его. p1 на данный момент не существует, так как право собственности перешло к p3.

Нам нужно использовать функцию Rust 'format!, которая будет «занимать» ячейки памяти p1 и p2 для создания p3 и не повлияет на владение какой-либо переменной:

Теперь мы повторили поведение Python. Оба должны вывести это:

Duke Nukem Duke Nuke

Теперь давайте кратко рассмотрим, как можно распечатать отдельные символы, индексы и часть строки (фрагмент):

Как видите, код выглядит очень похоже. В Rust мы используем метод chars() для получения одного символа и char_indices() для получения индекса и символа. Нарезка практически идентична.

Часть 2: Модуль Rand (ящик)

Теперь, когда мы вооружены базовыми знаниями Rust String, давайте быстро перенесем две функции «DNA Toolkit». Для этого нам понадобится генератор случайных чисел. Модуль random является частью Python и может быть просто включен и использован, но в Rust нам нужно добавить его как зависимость, построить его, и только тогда мы сможем его использовать. Но это очень просто, как вы увидите.

Конфигурацию зависимостей проекта можно найти в Cargo.toml файле:

Прежде чем мы просто слепо добавим модуль, давайте посмотрим, как мы можем его найти. Откройте свой терминал, убедитесь, что он находится в папке «dna_engine», или воспользуйтесь встроенным терминалом и выполните cargo search для поиска rand модуля (контейнера).

В списке результатов поиска мы видим rand:

Теперь давайте добавим его в наш проект. Измените Cargo.toml Строку 7-8 файла, чтобы она выглядела так:

Вот и все. Когда мы закончим писать функцию (следующий сегмент), которая будет нуждаться в этом модуле, и запустим эту функцию, Rust увидит, что мы пытаемся использовать этот модуль. Он увидит, что мы добавили его в [dependencies], и скачает / построит его для нас.

Часть 3: Генерация случайных последовательностей ДНК

Давайте добавим два новых файла: dna_toolkit.py и dna_toolkit.rs. Здесь будут жить две наши новые функции.

Итак, вот наша функция генерации случайной последовательности ДНК: (мы используем цикл в Python вместо понимания списка в демонстрационных целях)

Структура очень похожа.

  • Строка 1: Мы импортируем / включаем случайные модули. выбор / ранд.
  • Строка 3: В Python нам не нужно указывать тип параметра и тип возвращаемого значения, в Rust это нужно делать. length: i32 сообщает компилятору, что мы будем передавать целое число в нашу функцию, а -> String часть сообщает компилятору, что мы вернем строку.
  • Строка 8: Код Python определенно выглядит чище, но Rust читать не намного сложнее. Мы просто просим модуль rand сгенерировать случайное число в диапазоне от 0 до length вектора, содержащего 4 символа нуклеотида. Мы push этот случайно выбранный символ в нашей строке. Единственный лишний бит, который у нас есть, - это thread_rng(). Согласно документации Rust: 'Получить лениво инициализированный локальный генератор случайных чисел потока, засеянный системой'

Часть 4: транскрипция ДНК в РНК

При транскрипции нам просто нужно заменить все нуклеотиды «Т» - тимина на «U» - урацил. Это делается с помощью только одной строчки кода в каждой, поскольку Python и Rust имеют для этого встроенные функции:

Легко, правда? Теперь давайте посмотрим на оба файла рядом:

А давайте попробуем добавить результат в наши main файлы, чтобы протестировать наши новые функции:

И вот результаты обоих:

А вот как должна выглядеть наша файловая структура:

Это все для этой статьи. Теперь мы знаем, как создавать строки и манипулировать ими в Rust. Как написать простую функцию, передать ей параметр и вернуть значение. Мы также узнали, как добавить модуль (ящик) в Rust и включить файлы в другие файлы.

Ссылки

Рекомендуемые книги по программированию на Rust

Видеоверсия этой статьи

GitLab: https://gitlab.com/RebelCoder/py_rust.git