vault backup: 2025-11-23 10:41:26

This commit is contained in:
Akulij 2025-11-23 10:41:26 +07:00
parent 6a19e45415
commit 2ac2b6de11
2 changed files with 75 additions and 3 deletions

View File

@ -5,10 +5,15 @@
- [x] Знает best practice для написания unsafe кода. ✅ 2025-11-19 - [x] Знает best practice для написания unsafe кода. ✅ 2025-11-19
- [x] Можно использовать примеры из third party крейтов. ✅ 2025-11-19 - [x] Можно использовать примеры из third party крейтов. ✅ 2025-11-19
Умеет писать unsafe код и безопасные обёртки для него.
Понимает причины UB и знает, как его не допустить. (откуда взялось, что значит и как может нанести вред работе программы)
Умеет проектировать безопасный интерфейс для unsafe кода.
Знает best practice для написания unsafe кода.
- [x] Начать с проблемы, когда компилятор не может гарантировать безопасность по памяти (но без этого невозможно написать программу), возможно из ub ✅ 2025-11-19 - [x] Начать с проблемы, когда компилятор не может гарантировать безопасность по памяти (но без этого невозможно написать программу), возможно из ub ✅ 2025-11-19
- [x] Рассказать про причины ub ✅ 2025-11-19 - [x] Рассказать про причины ub ✅ 2025-11-19
- [x] Рассказать, чем является unsafe, ответственность на программисте, про ub (НЕ является избавлением от borrow checker) ✅ 2025-11-19 - [x] Рассказать, чем является unsafe, ответственность на программисте, про ub (НЕ является избавлением от borrow checker) ✅ 2025-11-19
- [x] Рассказать про применение unsafe (взаимодействие с С, оптимизация (вспомнить небезопасную либу для бэкенда: rocket или actix), написание основы/базы языка) ✅ 2025-11-19 - [x] Рассказать про применение unsafe (взаимодействие с С, оптимизация (вспомнить небезопасную либу для бэкенда: rocket или actix), написание основыубазы языка) ✅ 2025-11-19
- [x] Определении функции unsafe если соблюдение инвариантов висит на пользователе (при написании такой функции смотреть - является ли сам интерфейс функции safe) ✅ 2025-11-19 - [x] Определении функции unsafe если соблюдение инвариантов висит на пользователе (при написании такой функции смотреть - является ли сам интерфейс функции safe) ✅ 2025-11-19
- [x] примеры из third party ✅ 2025-11-19 - [x] примеры из third party ✅ 2025-11-19
- [ ] Рассказать про бест практис при написании unsafe: - [ ] Рассказать про бест практис при написании unsafe:
@ -84,11 +89,25 @@ fn main() {
- Дереференс сырого указателя - Дереференс сырого указателя
- Вызов unsafe функции (к примеру, определенной через extern) - Вызов unsafe функции (к примеру, определенной через extern)
- Чтение/запись mut static переменной - Чтение/запись mut static переменной
- Имплиментация unsafe трейта - Имплементация unsafe трейта
- Доступ к полям union - Доступ к полям union
- Объявление extern блока (использовали его, когда объявляли C функции) - Объявление extern блока (использовали его, когда объявляли C функции)
- Использование unsafe атрибута (к примеру, `#[unsafe(no_mangle)]`, использованный ранее) - Использование unsafe атрибута (к примеру, `#[unsafe(no_mangle)]`, использованный ранее)
- Вызов функции с таким `target_feature`, который в текущей функции не определен (к примеру, вызов функции, для которой требуется расширение x86_64 `avx` из функции, для вызова которой не требуются никакие расширения) - Вызов функции с таким `target_feature`, который в текущей функции не определен (к примеру, вызов функции, для которой требуется расширение x86_64 `avx` из функции, для вызова которой не требуются никакие расширения)
Квиз - Множественный выбор
Какой функционал из этого НЕ добавляет ключевое слово unsafe, по сравнению с safe кодом?
Дереференс сырого указателя
Нет, дереференс сырого указателя можно выполнить только в unsafe блоке
Чтение mut static переменной
Нет, читать изменяемую статическую переменную, как и записывать в нее, можно только из unsafe блока
Дереференс ссылки
Правильно, дереференс ссылки можно выполнить и в safe и в unsafe коде
Запись в const static переменной
Нет, записывать в константную статическую переменную нельзя даже в unsafe блоке
Использование атрибута target_feature над функцией
Правильно, для использования такого аттрибута можно и без unsafe, так как сам по себе он не добавляет никакого неопределенного поведения
## Undefined Behavior ## Undefined Behavior
ОР - Понимает причины UB и знает, как его не допустить. (откуда взялось, что значит и как может нанести вред работе программы) ОР - Понимает причины UB и знает, как его не допустить. (откуда взялось, что значит и как может нанести вред работе программы)
@ -147,6 +166,8 @@ fn get_entities_at<T, const N: usize>(entities: &mut [T], indices: [usize; N]) -
- индексы могут больше, чем длина массива, что приведет к невалидным референсам, либо чтению/записи из не аллоцированной памяти, что привидет к панике - индексы могут больше, чем длина массива, что приведет к невалидным референсам, либо чтению/записи из не аллоцированной памяти, что привидет к панике
Такой код - проблема для безопасности всего кода, но мы можем её решить: пометив ее, как unsafe, либо сделав её логику безопасной Такой код - проблема для безопасности всего кода, но мы можем её решить: пометив ее, как unsafe, либо сделав её логику безопасной
### Путь первый - unsafe функция ### Путь первый - unsafe функция
ОР - Знает best practice для написания unsafe кода.
ОР - Умеет писать unsafe код и безопасные обёртки для него.
Если функция может принимать данные, которые могут быть невалидными и вызывать undefined behavior, такая функция должна быть помечена ключевым словом unsafe перед fn, чтобы сказать пользователю функции, что при ее использовании нужно дополнительное внимание, а компилятору - что здесь есть не безопасные операции, и поэтому быть вызвана такая функция может только в блоке unsafe. Теперь интерфейс выглядит так: Если функция может принимать данные, которые могут быть невалидными и вызывать undefined behavior, такая функция должна быть помечена ключевым словом unsafe перед fn, чтобы сказать пользователю функции, что при ее использовании нужно дополнительное внимание, а компилятору - что здесь есть не безопасные операции, и поэтому быть вызвана такая функция может только в блоке unsafe. Теперь интерфейс выглядит так:
```rust ```rust
/// Returns mutable references to many indices at once /// Returns mutable references to many indices at once
@ -224,6 +245,8 @@ pub const unsafe fn new_unchecked(ptr: *mut T) -> Self {
} }
``` ```
### Путь второй - оборачивание в безопасную обертку ### Путь второй - оборачивание в безопасную обертку
ОР - Знает best practice для написания unsafe кода.
ОР - Умеет писать unsafe код и безопасные обёртки для него.
unsafe как эффект, не должен распространяться бескончено вверх по графу вызова, иначе всё было бы unsafe. Поэтому в какой-то момент эти инварианты должны гарантироваться. Поэтому, давайте обернем наш код в безопасный, гарантировав инварианты: unsafe как эффект, не должен распространяться бескончено вверх по графу вызова, иначе всё было бы unsafe. Поэтому в какой-то момент эти инварианты должны гарантироваться. Поэтому, давайте обернем наш код в безопасный, гарантировав инварианты:
```rust ```rust
fn get_entities_at<T, const N: usize>(entities: &mut [T], indices: [usize; N]) -> Result<[&mut T; N], ()> { fn get_entities_at<T, const N: usize>(entities: &mut [T], indices: [usize; N]) -> Result<[&mut T; N], ()> {
@ -291,10 +314,55 @@ fn get_entities_at<T: Visibility, const N: usize>(entities: &mut [T], indices: [
Теперь при изменении можно просто перепроверить соблюдение инвариантов. Теперь при изменении можно просто перепроверить соблюдение инвариантов.
Но, оно нужно не только для этого. При написании такого комментария программист лишний раз подумает, какие инварианты нужно соблюсти. А ещё, упрощает нахождение бага, так как можно сравнить условия, когда возникает баг, с теми условиями, что прописаны в комментариями. И баг по безопасности будет возникать возникать только в unsafe блоках. Правда, при всплытии в одном месте, причиной возникновения может стать unsafe блок в совершенно другом месте. Поэтому, хотелось бы сузить количество кода для поиска бага, поэтому старайтесь уменьшать блоки unsafe. Но, оно нужно не только для этого. При написании такого комментария программист лишний раз подумает, какие инварианты нужно соблюсти. А ещё, упрощает нахождение бага, так как можно сравнить условия, когда возникает баг, с теми условиями, что прописаны в комментариями. И баг по безопасности будет возникать возникать только в unsafe блоках. Правда, при всплытии в одном месте, причиной возникновения может стать unsafe блок в совершенно другом месте. Поэтому, хотелось бы сузить количество кода для поиска бага, поэтому старайтесь уменьшать блоки unsafe.
### Хорош тот unsafe, которого нет ### Хорош тот unsafe, которого нет
ОР - Знает best practice для написания unsafe кода.
Лучшее, что можно сделать с unsafe: это не писать его. Если ту же логику можно можно написать в safe rust, то пишите её в safe rust (разве что, можно как небольшое исключение сказать про случай, когда с unsafe мы можем получить хороший прирост производительности в критическом для производительности месте). Стоит сто раз подумать, прежде чем писать unsafe код. Лучшее, что можно сделать с unsafe: это не писать его. Если ту же логику можно можно написать в safe rust, то пишите её в safe rust (разве что, можно как небольшое исключение сказать про случай, когда с unsafe мы можем получить хороший прирост производительности в критическом для производительности месте). Стоит сто раз подумать, прежде чем писать unsafe код.
## Проектирование безопасного интерфейса ## Проектирование безопасного интерфейса
ОР - Умеет проектировать безопасный интерфейс для unsafe кода.
ОР - Умеет писать unsafe код и безопасные обёртки для него.
Проверка инвариантов делает функцию безопасной, но ее интерфейс может оставаться весьма неудобным. Пользователю придется постоянно проверять, не возникло ли ошибки при выполнении, а еще все равно самому следить, что в функцию подаются валидные данные (так как даже ошибку лишний раз не хочется ловить в продакшене). Поэтому рекомендую сужать интерфейс типами, которые сами по себе будут давать гарантии (к примеру, для нашего кода гарантию на уникальность значений могу бы дать HashSet). Проверка инвариантов делает функцию безопасной, но ее интерфейс может оставаться весьма неудобным. Пользователю придется постоянно проверять, не возникло ли ошибки при выполнении, а еще все равно самому следить, что в функцию подаются валидные данные (так как даже ошибку лишний раз не хочется ловить в продакшене). Поэтому рекомендую сужать интерфейс типами, которые сами по себе будут давать гарантии (к примеру, для нашего кода гарантию на уникальность значений могу бы дать HashSet).
## Практика ## Практика
ОР - Умеет писать unsafe код и безопасные обёртки для него.
Для функции `strerror_r` из стандартной библиотеки C напишите extern (Для определения функции используйте `man strerror` на unix системах или [онлайн](https://www.man7.org/linux/man-pages/man3/strerror.3.html)), и безопасную обертку для него, используя безопасные типы. Пусть безопасная обертка будет принимать внешний буфер для записи, но для упрощения не будет делать релокацию при ошибке.
Подсказки:
Референсы являются безопасной версией сырых указателей
В случае ошибки возвращется std::io::Error
Чек-лист:
Интерфейс функции использует безопасные типы, валидные при любом значении и не позволяет вызывать undefined behavior
Для unsafe блоков прописаны SAFETY комментарии
Не возникает undefined behavior
Решение:
```rust
unsafe extern "C" {
fn strerror_r(errnum: c_int, strerrbuf: *mut c_char, buflen: usize) -> c_int;
}
fn os_error_stringify(code: i32, str: &mut String) -> Result<(), std::io::Error> {
let buf = str.as_mut_ptr();
let len = str.capacity();
// SAFETY: pointer is valid and len is within allocated buffer
let err = unsafe { strerror_r(code as c_int, buf as *mut _, len) };
match err {
0 => {
// SAFETY: len is within capacity and
// uninitialized bytes will be shrinked later
unsafe { str.as_mut_vec().set_len(len) };
let null_position = str.as_bytes().iter().position(|byte| *byte == 0).unwrap_or(0);
// SAFETY: null_position is within capacity and
// all items are before null are valid utf-8 bytes
unsafe { str.as_mut_vec().set_len(null_position) };
Ok(())
},
err => {
// Now string is probably invalid, so set len 0
// SAFETY: length 0 is always valid
// using vec set_len so we are not freeing buffer for reusability
unsafe { str.as_mut_vec().set_len(0) };
Err(std::io::Error::from_raw_os_error(err))
}
}
}
```
*Тут возможна вариация и с CString, но так как strerror_r записывает только ascii символы (совместимые с utf-8), то обычный String упрощает взаимодействие с интерфейсом*
**Начать с проблемы, когда компилятор не может гарантировать безопасность по памяти (но без этого невозможно написать программу), возможно из ub** **Начать с проблемы, когда компилятор не может гарантировать безопасность по памяти (но без этого невозможно написать программу), возможно из ub**
Допустим, на вход вашей функции Допустим, на вход вашей функции

View File

@ -1 +1,5 @@
Plugins lesson 3. Plugins lesson 3.
| | |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Умеет формировать API для подключения плагинов. <br> <br>Умеет создавать и собирать плагины в соответствии с заданным API. <br>(C) <br> <br>Умеет подключать и отключать плагины в рантайме. (загрузка библиотеки и ее выгрузка) | На примере торговых ботов для биржи создадим систему с возможностью подключения плагинов. Научимся формировать API для плагинов, собирать и подключать их в рантайме, а также управлять ими — включать и отключать по мере необходимости. <br> <br>Урок - практический. Студент после него сможет писать аналогичные решения, что мы и проверим в проекте. <br> <br>Торговый бот - контекст, в котором показываются примеры (не production bot) <br> <br>Придумать API |