
Комментарии 21
Классика – неэффективное решение на Rust сравнивают с эффективным на другом языке.
> Компромисс Раста в том, что язык агрессивно диктует вам архитектуру
Бред.
Защита от переполнения буфера - это не выравнивание и не оборачивание защитными страницами, а контроль размера записываемых в него данных.
Ну если вы не знаете какие функции у вас в программе куда пишут, то я даже не знаю...
Люк, используй итераторы
И не используй знаковые типы для индексации
Проверки целочисленного переполнения by default есть только в дебаг версии. В gcc можно с тем же успехом -fsanitize добавить, только оно по умолчанию отключено. (Кстати, а в ваших проектах есть -fwrapv, или живёте в мире UB?)
Если data не является сырым указателем то он имеет NonNull тип.
Если вы готовы зуб отдать, гарантируя что инварианты не будут нарушены - напишите unsafe блок, с get_unchecked
Как я понял, вторая часть вашего сниппета - это утрированный пример того, как выглядит аналог ржавому коду, если явно все проверки прописать. Так вот,
В расте итераторы есть и активно используются.
Как минимум - избавитесь от проверки
i >= 0.Да, значительная часть сишного кода такое не переживет. И да - реализации многих алгоритмов полагаются на то, что при переполнении будет выполнено приведение по модулю. И если они используют беззнаковый тип, то это не UB и 100% соответствие стандарту си.
Cистема типов и инварианты раста гарантируют, что
data(из вашего примера) никогда не будет null. Соответственно - никаких rt проверок.Просто так? Ничего против C или плюсов не имею. Но распробовав раст, возвращаться к ним не хочется.
Как минимум Redox написали…
И кстати, из любопытства погонял компилятор, так вот. Если первый пример кода на rust 1:1 переписать, то, при включенных оптимизациях
если N - константа, то цикл размотается вообще без проверок
если N - rt-переменная, то компилятор вынесет все проверки перед телом цикла
Но все это - такое себе. Компиляторы, конечно, нынче умные, но я не люблю на них в таких вопросах сильно полагаться. Здесь соптимизировал, там нет. И сиди гадай - где что.
#define N 12
char data[N] = "hello world";
for (int i = 0; i < N; i++) {
if (data[i] == 'e') {
data[i] = 'E';
}
}и что здесь неправильного?
Вот и выросло поколение программистов, для которых написать корректный код, который не вываливается за границы массива - что-то из области научной фантастики…
Ну так языки-альтернативы С/С++ появились как результат признания факта, что количество ошибок работы с памятью слабо коррелирует с квалификацией программиста. Потому что помимо пролома буфера есть use-after-free, double free, работа с неициализированными переменными в редко исполняющихся ветках программы и т.д. Да, какая-то часть таких ошибок предотвращается полезными привычками и дисциплиной самого программиста + разным тулингом в виде санитайзеров и статических анализаторов, но это не убирает главную причину таких ошибок - снижение внимательности и концентрации. Да и эволюционно человеческие мозги заточены на то, чтобы эффективно работать только с тем, что непосредственно перед глазами. Если в языке нет явного выражения владения и заимствования, а только неявное, в виде специфических "соглашений " и паттернов, то в какой-то момент кодовая база перестанет "влазить" в окно фокуса внимания разработчика и он так или иначе накосячит.
Так что не очень понятно, откуда такой хейт к языкам, которые зашивают корректную работу с памятью в синтаксис и семантику.
Самое забавное, что в индустрии есть двойные стандарты: в программистском сообществе только языки со строгой статической типизацией признаются подходящими для промышленной разработки ПО, хотя учитывая моду на "квалификационный снобизм", вообще не было бы неожиданностью, если звучали заявления что языки с динамической типизацией для настоящих профессионалов, а неучи просто не могут держать весь код в голове.
Так что не очень понятно, откуда такой хейт к языкам, которые зашивают корректную работу с памятью в синтаксис и семантику.
Какой хейт? Эта функциональность начиналась ещё с Паскаля. А может и раньше, не помню.
только языки со строгой статической типизацией признаются подходящими для промышленной разработки
Потому что у других проблемы с производительностью.
Вот и выросло поколение программистов, для которых написать корректный код, который не вываливается за границы массива - что-то из области научной фантастики…
Я тут как то упомянул, что портировал один проект с C# на C++, в комментариях спросили, что наверно сейчас там сплошные утечки памяти. Люди даже не представляют уже программирования без сборщиков мусора.
Подход mtproto.zig: Epoll + State Machine ]Старт сервера -> Предвыделяем массив слотов в памяти один разНовый клиент -> Берем свободный слот (O(1)) -> Читаем асинхронноКлиент отвалился -> Возвращаем слот в массив
Что мешает сделать ровно это же на С?
Если клиенты не отваливаются и их становится больше предвыделенного массива слотов -- тогда что?
Ее круто. Давай ещё. Туда эту семейку Сишную. Сегфолт теперь шиза дедов присмерти. Ееее
ну я могу так же сравнить 1 клон майнкрафта со своим - его на на зиг мой на раст, и у него и у меня бесконечные миры, вчера я потестил сборку мне планировщик нарисовал 4 гигабайта ), а моя сборка, 200-300 мегабайт, потом тоже интересно нагрузка будет расти, слоты покроют ну тысячу или 500, и вот уже будет близко даже к яве и это на зиг и раст. На самом деле и Раст и Зиг почти одинаково работают, то что вы описали слоты и 2 канала можно и на расте написать...
покажу свой пример из недавнего как я пинговал свою фс, ну без слотов
Скрытый текст
use std::{
collections::HashMap,
fs::File,
io::{BufRead, BufReader},
os::unix::fs,
sync::{
Arc,
mpsc::{self, Receiver, Sender},
},
thread,
};
//
fn filer_test(file1: &str, st: &str) {
let file = File::open(file1).expect("msg");
let reader = BufReader::new(&file);
// let y = file.metadata().expect("msg").is_file();
let mut chanell: Chanell = Chanell::new();
chanell.recieve();
// if y {
for (l, line) in reader.lines().enumerate() {
if let Ok(line_content) = line {
if line_content.contains(st) {
let data = Res1::new(file1.to_string(), line_content);
chanell.spawn(data);
thread::sleep(std::time::Duration::from_millis(200));
}
}
}
// }
}
fn test_filler(p: &str, f: &str) {
let files = std::fs::read_dir(p)
.expect("msg")
.filter_map(|e| e.ok())
.filter(|e| e.path().is_file())
.collect::<Vec<_>>();
for i in files {
let r = String::from(p) + i.file_name().into_string().expect("msg").as_str();
filer_test(r.as_str(), f);
}
}
struct Chanell {
sender: Sender<Arc<Res1>>,
receiver: Option<Receiver<Arc<Res1>>>,
}
impl Chanell {
fn new() -> Self {
let (tx, rx) = mpsc::channel();
Self {
sender: tx,
receiver: Some(rx),
}
}
fn spawn(&self, data: Res1) {
let tx1 = self.sender.clone();
let a = Arc::new(data);
thread::spawn(move || {
tx1.send(a).expect("Failed to send");
});
}
fn recieve(&mut self) {
if let Some(rx) = self.receiver.take() {
thread::spawn(move || {
// Теперь rx принадлежит этому потоку
while let Ok(received) = rx.recv() {
for (key, value) in &received.n {
println!("Got Key: {}, Value: {}", key, value);
}
}
println!("Channel closed");
});
}
}
}
struct Res1 {
n: HashMap<String, String>,
}
impl Res1 {
fn new(st: String, st1: String) -> Self {
let mut n1 = HashMap::new();
n1.insert(st, st1);
Self { n: n1 }
}
}
fn main() {
let args: Vec<String> = std::env::args().collect();
// ./app ~/ apt - директория с закрывашкой /
if args.len() == 3 {
let first_arg = &args[1];
let second_arg = &args[2];
println!("{} {}", first_arg, second_arg);
test_filler(first_arg, second_arg);
}
}
тут 1 канал, а по вашему примеру для флексити нужно 2 канала, там кстати на С как не раскручивайся даже с калбеками память отестся, и мой бинарник который я привел весит 600 килобайт).
Когда на Rust уже всё переписали