9.7 KiB
-
Умеет писать unsafe код и безопасные обёртки для него.
-
Понимает причины UB и знает, как его не допустить. (откуда взялось, что значит и как может нанести вред работе программы)
-
для автора - проверка ОРа квизами, включая примеры кода.
-
Умеет проектировать безопасный интерфейс для unsafe кода.
-
Знает best practice для написания unsafe кода.
-
Можно использовать примеры из third party крейтов.
-
Начать с проблемы, когда компилятор не может гарантировать безопасность по памяти (но без этого невозможно написать программу), возможно из ub
-
Рассказать про причины ub
-
Рассказать, чем является unsafe, ответственность на программисте, про ub (НЕ является избавлением от borrow checker)
-
Рассказать про применение unsafe (взаимодействие с С, оптимизация (вспомнить небезопасную либу для бэкенда: rocket или actix), написание основы/базы языка)
-
Определении функции unsafe если соблюдение инвариантов висит на пользователе (при написании такой функции смотреть - является ли сам интерфейс функции safe)
-
примеры из third party
-
Рассказать про бест практис при написании unsafe:
- Лучший unsafe - отсутствующий unsafe
- Уменьшение зоны unsafe (легче найти баг + проще просчитать ub)
- Safety
- SAFETY
- assert_unsafe_precondition!
- ...
-
Практика
-
Лайфтаймы как помошники при взаимодействии с 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 коде. Давайте же расширим свои знания, и научимся взаимодействовать с таким кодом. В прошлом уроке в практическом задании вам встречался такой код:
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, но так необходимо при взаимодействии с внешними функциями, так для них компилятор никак не может гарантировать безопасность.
Взаимодействие с сырым указателем
Если же сырой указатель не дает
Начать с проблемы, когда компилятор не может гарантировать безопасность по памяти (но без этого невозможно написать программу), возможно из 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