моя контактная информация
Почтамезофия@protonmail.com
2024-07-12
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
Всем привет
Сегодня я завершил учебный лагерь по разработке ОС для автономного вождения 2024 года — четвертый этап начального лагеря — учебное пособие
День 2. Практика языка Rust с шорохами-семантика перемещения
https://doc.rust-lang.org/stable/book/ch04-00-understanding-ownership.html
Когда я отправляю код, я получаю сообщение о том, что у меня нет разрешения. Как мне выйти?
ацитон
Эталонная конфигурация среды разработки
https://rcore-os.cn/arceos-tutorial-book/ch01-02.html
моя тема
https://github.com/cicvedu/rustlings-semester-4-watchpoints
Создайте ключ ssh для клонирования кода github через ssh. В среде Linux используйте команду ssh-keygen -t rsa -b 4096 -C «ваш почтовый ящик», чтобы создать ssh-ключ. Просто нажмите Enter, чтобы использовать все следующие параметры. Затем используйте cat ~/.ssh/id_rsa.pub.
Rust имеет несколько базовых типов, которые напрямую реализованы в компиляторе. В этом разделе мы рассмотрим самые важные из них.
Тип среза Срезы позволяют ссылаться на непрерывную последовательность элементов в коллекции, а не на всю коллекцию.
Срез — это своего рода ссылка, поэтому он не имеет права собственности.
В Rust срез — это представление, которое ссылается на последовательность смежных элементов массива или другого среза. Чтобы создать срез, необходимо указать начальную и конечную позиции среза (не включая индекс конечной позиции).в массивеa
если хочешь получить[2, 3, 4]
Для этого фрагмента вам нужно начать с индекса 1 и закончить индексом 4 (исключая индекс 4).
Вот как исправить тестовый код:
#[test]
fn slice_out_of_array() {
let a = [1, 2, 3, 4, 5];
// 从索引 1 开始,到索引 4 结束的切片
let nice_slice = &a[1..4];
assert_eq!([2, 3, 4], nice_slice);
}
В этом примере&a[1..4]
Создал массив изa
Срез начинается с индекса 1 и заканчивается индексом 3, поскольку синтаксис среза в Rust представляет собой интервал, закрытый слева и открытый справа (включая начало, но не конец).такnice_slice
Он содержит массивa
середина[2, 3, 4]
。
Введите «Шуршащие часы» в командной строке, чтобы начать циклический эксперимент по редактированию кода.
Понимание права собственности
Право собственности — самая уникальная особенность Rust, имеющая глубокие последствия для всего остального языка.
Это позволяет Rust гарантировать безопасность памяти без необходимости использования сборщика мусора.
Что такое право собственности?
Правила владения Сначала давайте рассмотрим правила владения. Помните об этих правилах, когда будете работать с примерами, которые их иллюстрируют:
Сам пример кода корректен и демонстрирует концепции владения и копирования в Rust. Однако, если вы хотите убедиться, что ваш код компилируется и работает, а также хотите внести некоторые возможные улучшения или исправления, вот несколько советов:
убеждаться takes_ownership
иmakes_copy
функционировать вmain
функция, определенная ранее, потому что функции в Rust необходимо сначала объявить, а затем использовать.
добавлять заметки, объяснить, почему s
существоватьtakes_ownership
нельзя использовать после вызова, иx
существоватьmakes_copy
Его все еще можно использовать после вызова.
добавить в main
тип возвращаемого значения функции, хотя в простых программах это не обязательно, это хорошая практика программирования.
Использование инструментов форматирования Rust,например rustfmt
, чтобы сохранить код в чистоте.
Вот модифицированный код, основанный на приведенных выше предложениях:
fn takes_ownership(some_string: String) { // 函数定义在 main 之前
println!("{}", some_string);
} // some_string 的作用域结束,调用 Drop
fn makes_copy(some_integer: i32) { // 函数定义在 main 之前
println!("{}", some_integer);
} // some_integer 的作用域结束,没有调用 Drop,因为 i32 是 Copy 类型
fn main() -> () { // 明确 main 函数的返回类型为 ()
let s = String::from("hello"); // s 拥有 "hello" 的所有权
takes_ownership(s); // s 的值移动到 takes_ownership 函数
// 下面的代码尝试使用 s 将会导致编译错误,因为 s 的值已经移动
// let len = s.len(); // 错误:s 已移动
let x = 5; // x 包含值 5
makes_copy(x); // x 的值被复制到 makes_copy 函数
// x 仍然有效,因为 i32 是 Copy 类型
// x 仍然可以使用
println!("x is {}", x);
}
В этой обновленной версии мы добавили main
тип возвращаемого значения функции()
,это означает main
Функция не возвращает значения.Кроме того, добавлены комментарии, объясняющие, почемуs
существоватьtakes_ownership
нельзя использовать после вызова, иx
существоватьmakes_copy
Все еще действителен после звонка.Кроме того, мы показываем попыткиtakes_ownership
Использовать после звонкаs
Примеры, которые могут вызвать ошибки компиляции, иx
существоватьmakes_copy
Примеры, которые можно использовать после вызова.
Векторы — одна из наиболее используемых структур данных Rust.
В других языках программирования их бы просто называли массивами, но поскольку Rust работает на несколько более низком уровне, массив в Rust хранится в стеке (то есть он не может увеличиваться или уменьшаться, и его размер должен быть известен во время компиляции), а вектор хранится в куче (где эти ограничения не применяются).
Векторы — это немного более поздняя глава в книге, но мы думаем, что они достаточно полезны, чтобы поговорить о них немного раньше. Мы поговорим о другой полезной структуре данных, хэш-картах, позже.
vec_map
Функция состоит в том, чтобы принятьVec<i32>
тип (т. е. вектор, содержащий 32-битное целое число), затем возвращает новоеVec<i32>
, каждый элемент в этом новом векторе в два раза больше соответствующего элемента в исходном векторе.
Конкретно,vec_map
в функцииmap
иcollect
Метод работает следующим образом:
**v.iter()
**: Этот метод создает итератор, который проходит v
каждый элемент вектора.iter
Итератор, возвращаемый методом, предоставляет неизменяемую ссылку на элементы вектора.
**.map(|element| { element * 2 })
**:map
Метод принимает замыкание (анонимную функцию), в этом случае замыкание принимает один параметр.element
(неизменяемая ссылка на элемент в векторе), а затем возвращает значение этого элемента, умноженное на 2.Операции внутри замыканийelement * 2
Фактически вы разыменовываете данные, на которые указывает ссылка (через*
оператор) и выполнять операции умножения.
**.collect()
**:map
Метод возвращает ленивый итератор, который применяет операции замыкания одну за другой, но не собирает результаты немедленно.collect
метод вызывается для передачи этихmap
Обработанные элементы собираются в новыйVec<i32>
в векторе.
использовать map
иcollect
Преимущество в том, что они позволяют выполнять операции над каждым элементом коллекции декларативным способом и собирать результаты в новую коллекцию без необходимости писать явные циклы. Таким образом, код становится более кратким и более соответствует стилю Rust.
Вот как использовать vec_map
Пример функции:
fn main() {
let original_vec = vec![1, 2, 3, 4];
let doubled_vec = vec_map(&original_vec);
println!("{:?}", doubled_vec); // 这将打印:[2, 4, 6, 8]
}
В этом примереvec_map
перениматьoriginal_vec
ссылка, создание нового вектораdoubled_vec
, который содержит удвоенное значение каждого элемента исходного вектора и выводит его.
Эти упражнения адаптированы из pnkfelix's Учебник по Rust -- Спасибо, Феликс!!!
Для этого раздела ссылки на книги особенно важны.
В Rust существует правило для изменяемых ссылок: в любой момент времени вы можете иметь любое количество неизменяемых ссылок или не более одной изменяемой ссылки. Это правило обеспечивает безопасность памяти и предотвращает гонки данных.
В предоставленном коде есть проблема:main
Функция попыталась создать две изменяемые ссылки.y
иz
, оба указывают на одну и ту же переменную x
. Это недопустимо, поскольку правила заимствования Rust не допускают множественных изменяемых ссылок на одну и ту же переменную в одной области видимости. Вот почему код не компилируется.
Чтобы решить эту проблему, вам необходимо убедиться, что в любой момент времени существует только одна изменяемая ссылка. Вот фиксированный код:
fn main() {
let mut x = 100;
{
let y = &mut x;
*y += 100; // 此时 x 的值变为 200
} // 'y' 的作用域结束,可变引用被销毁
{
let z = &mut x; // 可以创建新的可变引用,因为 'y' 已经不存在了
*z += 1000; // 此时 x 的值变为 1200
} // 'z' 的作用域结束,可变引用被销毁
assert_eq!(x, 1200); // 断言 x 的值是 1200,这是正确的
}
В этой фиксированной версии мы гарантируем, что в любой момент времени существует только одна изменяемая ссылка, создавая внутреннюю область видимости.Сначала мы создалиy
область видимости, над ней выполняется операция, а затем эта область действия заканчивается,y
Более не действителен.После этого мы создалиz
объем, опять же дляx
Выполнять операции. Это соответствует правилам заимствования Rust, поэтому код компилируется и работает как положено.
Это сообщение об ошибке компиляции указывает на проблему жизненного цикла кода Rust.Проблема вstring_uppercase
В реализации функции она пытается присвоить временную величину ссылке, что недопустимо.
Вот строка кода, которая идет не так:
data = &data.to_uppercase();
В этой строке кодаdata.to_uppercase()
СоздалString
Новый экземплярdata
взял взаймы.Однако попробуйте присвоить ссылку на это временное значениеdata
является незаконным, поскольку временное значение уничтожается сразу в конце оператора, иdata
Требует более длительного срока службы, чем временные значения.
Чтобы решить эту проблему, нам нужно изменить string_uppercase
Функция, которая принимает изменяемую ссылку и вызывает ее непосредственно для данных, на которые указывает эта ссылка.make_uppercase()
метод вместо того, чтобы пытаться переназначить. Вот фиксированный код:
fn string_uppercase(data: &mut String) {
data.make_uppercase(); // 直接在 data 上调用 make_uppercase 方法
println!("{}", data);
}
В этой исправленной версии мы удалили mut data: &String
серединаmut
,потому что data
Уже изменяемая ссылка.Тогда мы непосредственноdata
позвонитьmake_uppercase()
, поэтому проблем с временными значениями не будет.make_uppercase()
Метод будет измененdata
ориентиры наString
экземпляр без создания новогоString
。
также,main
Вызов внутри функции также необходимо соответствующим образом обновить, чтобы передать изменяемую ссылку:
fn main() {
let mut data = "Rust is great!".to_string();
string_uppercase(&mut data); // 传递一个可变引用
println!("{}", data); // 打印修改后的字符串
}
Сейчас,string_uppercase
функция принимаетdata
изменяемую ссылку, измените ее напрямую иmain
в функцииdata
изменчив и может бытьstring_uppercase
модифицированный. Таким образом, код компилируется и работает как положено.
/**
【昵称】小王同学
【坐标】山东
【自我介绍】
1. 高级后端工程师,擅长c++并发编程。
2. 专注分布式存储,分布式数据库。
3. 时间的践行者,立志成为一名讲师。
【我的成绩】
1. 为了走出农村,2次高考
一次考研失败,
遇挫之后不知道干什么,开启被动之旅。
2. 为了找到合适工作,
深入研究c++书籍和leetcode 200题目
3. 为了提高项目能力,参与开源项目建设。
4. 为了把简单事情说清楚/要干啥
按照《只管去做》,《福格行为模型>>方法。
纸上得来终觉浅,绝知此事要躬行
做一个践行者。
【我能提供】
1. 后端程序员的简历优化+就业辅导+职业规划
2. 全栈工程师(c++,rust,go,python )项目开发
3. 一年践行12本书践行记录。
【希望一起解决什么,开启破圈之旅】
1. 交接更多朋友,抱团取暖。
寻找意义本身就更加有意义。
2. 无法做整个系统,聚焦一个模块
道可道也,非恒道也
名可名也,非恒名也。
无名 万物之始也
有名 万物之母也
别想太多,只管去做,躬身入局
链接我: # + v(github):watchpoints
#众号:后端开发成长指南
**/
Эту статью написал mdniceМультиплатформенная публикация