vault backup: 2025-11-23 10:41:26
This commit is contained in:
parent
6a19e45415
commit
2ac2b6de11
72
4.2/2.md
72
4.2/2.md
@ -5,10 +5,15 @@
|
||||
- [x] Знает best practice для написания unsafe кода. ✅ 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] Рассказать, чем является 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] примеры из third party ✅ 2025-11-19
|
||||
- [ ] Рассказать про бест практис при написании unsafe:
|
||||
@ -84,11 +89,25 @@ fn main() {
|
||||
- Дереференс сырого указателя
|
||||
- Вызов unsafe функции (к примеру, определенной через extern)
|
||||
- Чтение/запись mut static переменной
|
||||
- Имплиментация unsafe трейта
|
||||
- Имплементация unsafe трейта
|
||||
- Доступ к полям union
|
||||
- Объявление extern блока (использовали его, когда объявляли C функции)
|
||||
- Использование unsafe атрибута (к примеру, `#[unsafe(no_mangle)]`, использованный ранее)
|
||||
- Вызов функции с таким `target_feature`, который в текущей функции не определен (к примеру, вызов функции, для которой требуется расширение x86_64 `avx` из функции, для вызова которой не требуются никакие расширения)
|
||||
Квиз - Множественный выбор
|
||||
Какой функционал из этого НЕ добавляет ключевое слово unsafe, по сравнению с safe кодом?
|
||||
Дереференс сырого указателя
|
||||
Нет, дереференс сырого указателя можно выполнить только в unsafe блоке
|
||||
Чтение mut static переменной
|
||||
Нет, читать изменяемую статическую переменную, как и записывать в нее, можно только из unsafe блока
|
||||
Дереференс ссылки
|
||||
Правильно, дереференс ссылки можно выполнить и в safe и в unsafe коде
|
||||
Запись в const static переменной
|
||||
Нет, записывать в константную статическую переменную нельзя даже в unsafe блоке
|
||||
Использование атрибута target_feature над функцией
|
||||
Правильно, для использования такого аттрибута можно и без unsafe, так как сам по себе он не добавляет никакого неопределенного поведения
|
||||
|
||||
|
||||
|
||||
## Undefined Behavior
|
||||
ОР - Понимает причины UB и знает, как его не допустить. (откуда взялось, что значит и как может нанести вред работе программы)
|
||||
@ -147,6 +166,8 @@ fn get_entities_at<T, const N: usize>(entities: &mut [T], indices: [usize; N]) -
|
||||
- индексы могут больше, чем длина массива, что приведет к невалидным референсам, либо чтению/записи из не аллоцированной памяти, что привидет к панике
|
||||
Такой код - проблема для безопасности всего кода, но мы можем её решить: пометив ее, как unsafe, либо сделав её логику безопасной
|
||||
### Путь первый - unsafe функция
|
||||
ОР - Знает best practice для написания unsafe кода.
|
||||
ОР - Умеет писать unsafe код и безопасные обёртки для него.
|
||||
Если функция может принимать данные, которые могут быть невалидными и вызывать undefined behavior, такая функция должна быть помечена ключевым словом unsafe перед fn, чтобы сказать пользователю функции, что при ее использовании нужно дополнительное внимание, а компилятору - что здесь есть не безопасные операции, и поэтому быть вызвана такая функция может только в блоке unsafe. Теперь интерфейс выглядит так:
|
||||
```rust
|
||||
/// 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. Поэтому в какой-то момент эти инварианты должны гарантироваться. Поэтому, давайте обернем наш код в безопасный, гарантировав инварианты:
|
||||
```rust
|
||||
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, которого нет
|
||||
ОР - Знает best practice для написания unsafe кода.
|
||||
Лучшее, что можно сделать с unsafe: это не писать его. Если ту же логику можно можно написать в safe rust, то пишите её в safe rust (разве что, можно как небольшое исключение сказать про случай, когда с unsafe мы можем получить хороший прирост производительности в критическом для производительности месте). Стоит сто раз подумать, прежде чем писать unsafe код.
|
||||
## Проектирование безопасного интерфейса
|
||||
ОР - Умеет проектировать безопасный интерфейс для unsafe кода.
|
||||
ОР - Умеет писать unsafe код и безопасные обёртки для него.
|
||||
Проверка инвариантов делает функцию безопасной, но ее интерфейс может оставаться весьма неудобным. Пользователю придется постоянно проверять, не возникло ли ошибки при выполнении, а еще все равно самому следить, что в функцию подаются валидные данные (так как даже ошибку лишний раз не хочется ловить в продакшене). Поэтому рекомендую сужать интерфейс типами, которые сами по себе будут давать гарантии (к примеру, для нашего кода гарантию на уникальность значений могу бы дать 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**
|
||||
Допустим, на вход вашей функции
|
||||
|
||||
6
4.2/3.md
6
4.2/3.md
@ -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 |
|
||||
Loading…
x
Reference in New Issue
Block a user