- [x] Умеет писать unsafe код и безопасные обёртки для него. ✅ 2025-11-20
- [x] Понимает причины UB и знает, как его не допустить. (откуда взялось, что значит и как может нанести вред работе программы) ✅ 2025-11-19
- [x] для автора - проверка ОРа квизами, включая примеры кода. ✅ 2025-11-19
- [ ] Умеет проектировать безопасный интерфейс для unsafe кода.
- [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 если соблюдение инвариантов висит на пользователе (при написании такой функции смотреть - является ли сам интерфейс функции safe) ✅ 2025-11-19
- [x] примеры из third party ✅ 2025-11-19
- [ ] Рассказать про бест практис при написании unsafe:
	- [x] Лучший unsafe - отсутствующий unsafe ✅ 2025-11-19
	- [x] Уменьшение зоны unsafe (легче найти баг + проще просчитать ub) ✅ 2025-11-19
	- [x] Safety ✅ 2025-11-19
	- [x] SAFETY ✅ 2025-11-19
	- [x] assert_unsafe_precondition! ✅ 2025-11-19
	- [ ] ...
- [ ] Практика
- [ ] Лайфтаймы как помошники при взаимодействии с c abi
- [ ] Drop

Почему unsafe внутри unsafe fn:
фн показывает, что инварианты должен соблюдать пользователь функции
ансейф блок же показывает скоуп, где нужно искать баги (где они и будут тк анйсейф операция)
подводный камень:
при ансейф баг может возникать в любом из блоков анйсефа, даже если он возникает в другом месте

ub: из-за различий архитектру цп и ос и для оптимизаций

еслм возникает баг, значит неправильно реализованы инварианты в одном из unsafe блоков с которым взаимодействовали (даже в совершенно другом месте)

Возможно рассказать еще про это все?
- pointers
- nonnull
- phantomdata (инвариантность/ковариантность и тд)
- unsafecell
- send/sync + unsafe trait markers (мб практический пример: написание собстенного mutex)
- unsafe traits
- 

При описании бест практис про Safety, привести пример, почему нужно (а еще потому, что при изменении логики функции самим тоже нужно следить, какие инварианты висят за пользователем)
Практика: подумайте над тем, какие инварианты тут должны быть соблюдены и какие из них не соблюдены. или пропробовать найти несоблюденный инвариант и "взломать" программу?)
допустим, нам нужно дя создания сокета узнать, можем ли мы использовать tcp/udp/icmp/etc, для этого используем сискол WSAEnumProtocolsA


# Start
В прошлом уроке мы научились использовать C функции в своем rust коде. Давайте же расширим свои знания, и научимся взаимодействовать с таким кодом. В прошлом уроке в практическом  задании вам встречался такой код:
```rust
include!(concat!(env!("OUT_DIR"), "/bindgen.rs"));

use std::ffi::{CStr, CString};

const TEST_JSON: &CStr = c"{
    \"meaning_of_life\": 42
}";

fn main() {
    let json: *mut cJSON = unsafe { cJSON_Parse(TEST_JSON.as_ptr()) };

    let json_str = unsafe { cJSON_PrintUnformatted(json) };
    let json_str = unsafe { CString::from_raw(json_str) };
    let json_str = json_str.to_str().unwrap();
    assert_eq!(json_str, r#"{"meaning_of_life":42}"#);

    let meaning_of_life = unsafe { cJSON_GetObjectItem(json, c"meaning_of_life".as_ptr()) };
    let meaning_of_life = unsafe { cJSON_GetNumberValue(meaning_of_life) };
    println!("Meaning of life: {}", meaning_of_life);
    assert_eq!(meaning_of_life, 42f64);
}
```
У json обозначен не встречавшийся до этого тип `*mut _`
## Сырые указатели
Вы уже неоднократно пользовались референсами в своем коде. В этих типах компилятор следит за временем жизни, чтобы не обратится к памяти после освобождения, а так же за валидностью памяти. Но, когда дело доходит до вызова внешней функции компилятор уже будет не способен отследить время жизни и валидность памяти. Поэтому в rust есть аналогичный тип - сырые указатели. Он унаследован по синтаксису и смыслу от своих предшественников (в частности от С), поэтому его описание выглядит так:
`*const T` - для неизменяемого указателя на тип T
`*mut T` - для изменяемого указателя на тип T
Внутри себя он, точно так же, как и референс, содержит адрес на память и опционально метадату, но сырой указатель не дает абсолютно никаких гарантий по памяти. Это нетипично для rust, но так необходимо при взаимодействии с внешними функциями, так для них компилятор никак не может гарантировать безопасность.
## Взаимодействие с сырым указателем
С появлением сырых указателей возникает важный нюанс: раз компилятор не может контролировать корректность памяти, ответственность за её корректное использование будет ложится на программиста. Так как такой функционал необходим, но перечит памяти-безопасности языка, такой код отделяется в специальный unsafe блок.
## Unsafe
Ключевое слово unsafe означает потенциально не безопасный код с точки зрения компилятора. При написании блока unsafe ответственность за правильность и безопасность ложится на программиста. Но, при этом, rust внутри блока не превращается в некий аналог C. Компилятор просто разрешает программисту выполнять некоторые {{дополнительные действия}}[https://doc.rust-lang.org/reference/unsafety.html], в которые входят:
- Дереференс сырого указателя
- Вызов unsafe функции (к примеру, определенной через extern)
- Чтение/запись mut static переменной
- Имплементация 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 и знает, как его не допустить. (откуда взялось, что значит и как может нанести вред работе программы)
Как думаете, что произойдет, если какой-нибудь из пунктов выше выполнится не так, как задумано? Возьмем пример: если вы пришли из C, то хорошо знаете функцию malloc: при вызове аллоцирует память нужного размера, но если выделить память не получиться, то вернется нулевой указатель. Если забыть проверить, что указатель не нулевой, и прочитать значение по нему, то что произойдет? Это поведение предсказать невозможно, а в стандарте языка (что C, что Rust) это поведение обозначено как **неопределенное поведение**.
**Неопределенное поведение** - свойство какого-то действия (список этих действий определяется языком), при выполнении которого последствия не определены стандартом языка.
Для rust этот список можно посмотреть {{тут}}[doc.rust-lang.org/reference/behavior-considered-undefined.html]. Хоть этот список и больше списка действий, возможных в блоке unsafe, все они являются последствиями неправильного выполнения unsafe действий. Поэтому главная задача программиста при использовании блока unsafe: cледить за тем, чтобы не возникали неопределенные поведения.
## Зачем все это?
При изучении undefined behavior, может возникнуть вопрос: нельзя ли определить это поведение, и упростить программистам жизнь? Ответом на этот вопрос является то, что создатели языков их хотели бы не создавать ub, но это не является возможным. К примеру, для деления на ноль поведение определить невозможно, ведь это поведение неопределено и в самой математике. Так же, невозможно определить и результат гонки данных, изученой в теме по многопоточности, ведь не известно, в каком порядке потоки будут исполнены.
Еще одним важным вопросом является отличие поведения платформ. К примеру, x86_64 при делении на ноль выдаст исключение. В это же время arm может выдать любое мусорное значение (в частности, на apple silicon выдает 0).
И последнее: производительность. Да, это поведение можно было бы определить, к примеру, сделав дополнительные runtime проверки, к примеру, вместо такой функции:
```rust
fn in_five(x: u32) -> u32 {
	5 / x
}
```
Сделать такую:
```rust
fn in_five(x: u32) -> u32 {
	if x == 0 {
		panic!("attempt to divide by zero");
	}
	5 / x
}
```
Но, из-за таких проверок код может стать гораздо медленнее, а нам нужна производительность.
Заметка:
*Если вы обратили внимание, что мы производим деление, где может возникнуть ub, в safe коде, вы хорошо поняли, когда код не безопасен. В реальности, создатели rust по умолчанию решили сделать деление безопасным, поэтому при каждом таком делении производится проверка на ноль, и в выходной программе функция будет выглядеть примерно так, как написано выше с проверкой на ноль. При этом, возможность выполнить деление без проверки возможно через std::intrinsics::unchecked_div, которая помечена как unsafe.*
## Написание unsafe кода
Давайте попробуем эксперимент: у нас есть игровой движок, который хранит список игровых энтити в векторе. И нам нужно за раз достать несколько из них по определенным индексам. Поэтому у нас в коде будет такая функция:
```rust
/// Returns mutable references to many indices at once
fn get_entities_at<T, const N: usize>(entities: &mut [T], indices: [usize; N]) -> [&mut T; N] {
    unsafe {
	    // преобразование референса в указатель, чтобы можно было вызвать
	    // get_unchecked_mut в цикле
	    let entities: *mut [T] = entities;
        // просто аллокация памяти на стеке с неинициализироваными значениями
        // Небольшая оптимизация, так как лишний раз не записываем нули,
        // которые потом все равно будут перезаписаны
        let mut output: [&mut T; N] = MaybeUninit::uninit().assume_init();
        for i in 0..N {
            let index: usize = *indices.get_unchecked(i);
            output[i] = (&mut *entities).get_unchecked_mut(index);
        }

        output
    }
}
```

Подумайте, какие ошибки допущены в этой функции? Какие неправильные данные можно в нее подать, что код станет не безопасным?

*Тогл*
Ответ:
- индексы могут пересекаться, что приведет к содержанию нескольких мутабельных референсов на одно и то же значение
- индексы могут больше, чем длина массива, что приведет к невалидным референсам, либо чтению/записи из не аллоцированной памяти, что привидет к панике
Такой код - проблема для безопасности всего кода, но мы можем её решить: пометив ее, как unsafe, либо сделав её логику безопасной
### Путь первый - unsafe функция
ОР - Знает best practice для написания unsafe кода.
ОР - Умеет писать unsafe код и безопасные обёртки для него.
Если функция может принимать данные, которые могут быть невалидными и вызывать undefined behavior, такая функция должна быть помечена ключевым словом unsafe перед fn, чтобы сказать пользователю функции, что при ее использовании нужно дополнительное внимание, а компилятору - что здесь есть не безопасные операции, и поэтому быть вызвана такая функция может только в блоке unsafe. Теперь интерфейс выглядит так:
```rust
/// Returns mutable references to many indices at once
unsafe fn get_entities_at<T, const N: usize>(entities: &mut [T], indices: [usize; N]) -> [&mut T; N] {
	// ...
}
```
Но тут у пользователя функции возникнут вопросы: почему функция помечена unsafe? раз помечена unsafe, то какие данные я НЕ могу передавать? (не забывайте, что функция должна быть понятна по ее интерфейсу, а не исходному коду). Поэтому, в rust в сроке документации функции принято писать, почему функция не безопасна и то, какие инварианты должны быть соблюдены при вызове этой функции, чтобы не возникало неопределенное поведение.
Для нашего примера это будет выглядеть так:
```rust
/// Returns mutable references to many indices at once
/// # Safety
/// * indices do not overlap
/// * indices are not out of bound of entities array
unsafe fn get_entities_at<T, const N: usize>(entities: &mut [T], indices: [usize; N]) -> [&mut T; N] {
	// ...
}
```
Теперь, пользователь функции будет знать, какие инварианты он будет должен соблюсти, чтобы код оставался безопасным. Но, к сожалению, люди не идеальны, и по случайности могут не соблюсти это инварианты. Поэтому хорошей практикой является проверка соблюдение этих условий в debug сборках, для этого используется макрос debug_assert (в будущем возможно станет стабильным более специфичный assert_unsafe_precondition или аналогичный макрос). Давайте напишем функцию проверки:
```rust
fn check_indicies_valid<const N: usize>(indices: &[usize; N], len: usize) -> bool {
    for index in indices {
        // out of bound
        if *index >= len {
            return false;
        }
        // index overlap
        if indices.iter().filter(|i| *i == index).count() > 1 {
            return false;
        }
    }
    true
}
```
И добавим функцию 
```rust
/// Returns mutable references to many indices at once
/// # Safety
/// * indices do not overlap
/// * indices are not out of bound of entities array
unsafe fn get_entities_at<T, const N: usize>(entities: &mut [T], indices: [usize; N]) -> [&mut T; N] {
    unsafe {
		// Check that indicies do not overlap and are not out of bound
        debug_assert!(check_indicies_valid(&indices, entities.len()));
        // преобразование референса в указатель, чтобы можно было вызвать
        // get_unchecked_mut в цикле
        let entities: *mut [T] = entities;
        // просто аллокация памяти на стеке с неинициализироваными значениями
        // Небольшая оптимизация, так как лишний раз не записываем нули,
        // которые потом все равно будут перезаписаны
        let mut output: [&mut T; N] = MaybeUninit::uninit().assume_init();
        for i in 0..N {
            let index: usize = *indices.get_unchecked(i);
            output[i] = (&mut *entities).get_unchecked_mut(index);
        }

        output
    }
}
```
Как пример похожей логики из стандартной библиотеки можно привести NonNull::new_unchecked (пример упрощен, оставлены только нужные для примера строки):
```rust
/// Creates a new `NonNull`.
/// # Safety
/// `ptr` must be non-null.
pub const unsafe fn new_unchecked(ptr: *mut T) -> Self {
	unsafe {
		assert_unsafe_precondition!(
			check_language_ub,
			"NonNull::new_unchecked requires that the pointer is non-null",
			(ptr: *mut () = ptr as *mut ()) => !ptr.is_null()
		);
		NonNull { pointer: ptr as _ }
	}
}
```
### Путь второй - оборачивание в безопасную обертку
ОР - Знает 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], ()> {
    if !check_indicies_valid(&indices, entities.len()) {
	    return Err(());
    }
	
	let entities = unsafe {
		// тот же код, что и был
	};
	
	Ok(entities)
}
```
Теперь гарантии происходят внутри самой функции. 
Спустя некоторое время вашему коллеге поступило ТЗ, что эта функция должна выдавать индексы только по энтити, которые видимы (пускай будет трейт с методом is_visible). Изменим код:
```rust
fn get_entities_at<T: Visibility, const N: usize>(entities: &mut [T], indices: [usize; N]) -> Result<[&mut T; N], ()> {
    if !check_indicies_valid(&indices, entities.len()) {
        return Err(());
    }
    
    let mut entities: Vec<_> = entities.into_iter().filter(|entity| entity.is_visible()).collect();
    let entities: &mut [&mut T] = entities.as_mut();

    let output = unsafe {
        debug_assert!(check_indicies_valid(&indices, entities.len()));
        // преобразование референса в указатель, чтобы можно было вызвать
        // get_unchecked_mut в цикле
        let entities: *mut [&mut T] = entities;
        // просто аллокация памяти на стеке с неинициализироваными значениями
        // Небольшая оптимизация, так как лишний раз не записываем нули,
        // которые потом все равно будут перезаписаны
        let mut output: [&mut T; N] = MaybeUninit::uninit().assume_init();
        for i in 0..N {
            let index: usize = *indices.get_unchecked(i);
            output[i] = (&mut *entities).get_unchecked_mut(index);
        }

        output
    };

    Ok(output)
}
```
Но останется ли такой код безопасным? Читающему код придётся снова разбирать, какие инварианты нужно соблюсти, чтобы код оставался безопасным. Но этого можно было бы избежать, если бы мы обозначали, какие инварианты мы соблюли. По примеру Safety у функций, у unsafe блоков обозначают соблюдены инварианты словом SAFETY:
```rust
fn get_entities_at<T: Visibility, const N: usize>(entities: &mut [T], indices: [usize; N]) -> Result<[&mut T; N], ()> {
    if !check_indicies_valid(&indices, entities.len()) {
        return Err(());
    }
    
    let mut entities: Vec<_> = entities.into_iter().filter(|entity| entity.is_visible()).collect();
    let entities: &mut [&mut T] = entities.as_mut();

	// SAFETY: the code is safe because we just checked that
	// indicies do not overlap and are not out of bound
    let output = unsafe {
		// тот же код, что и был
    };

    Ok(output)
}
```
Теперь при изменении можно просто перепроверить соблюдение инвариантов.
Но, оно нужно не только для этого. При написании такого комментария программист лишний раз подумает, какие инварианты нужно соблюсти. А ещё, упрощает нахождение бага, так как можно сравнить условия, когда возникает баг, с теми условиями, что прописаны в комментариями. И баг по безопасности будет возникать возникать только в 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**
 Допустим, на вход вашей функции 
 
**Рассказать про причины ub**
- как в математике есть неопределенности (к примеру, для деления на ноль), так и в языках программирования тоже есть свои неопределенности.
Именно для этого и было создана такая вещь, как неопределённое поведение. 
Неопределено, значит никогда не случается. Значит компилятор может оптимизировать код только соблюдая верность тех случаев, что не являются ub. (пример с nullptr дереф)

**Рассказать, чем является unsafe, ответственность на программисте, про ub (НЕ является избавлением от borrow checker)**

**Как про отдельную вещь рассказать про вызов unsafe функции**

**Рассказать про применение unsafe (взаимодействие с С, оптимизация (вспомнить небезопасную либу для бэкенда: rocket или actix), написание основы/базы языка)**

**Практика по пути**

**Мб рассказать про кейс с лайфтаймами**


- [ ] Определении функции unsafe если соблюдение инвариантов висит на пользователе (при написании такой функции смотреть - является ли сам интерфейс функции safe)
- [ ] примеры из third party
- [ ] Рассказать про бест практис при написании unsafe:
	- [ ] Лучший unsafe - отсутствующий unsafe
	- [ ] Уменьшение зоны unsafe (легче найти баг + проще просчитать ub)
	- [ ] Safety
	- [ ] SAFETY
	- [ ] assert_unsafe_precondition!
	- [ ] ...
- [ ] Практика
- [ ] Лайфтаймы как помошники при взаимодействии с c abi
- [ ] Drop