LINUX.ORG.RU

C++ vs Rust: правда ли, что Rust тупо сложнее крестов в базовых сценариях применения?

 


0

6

Если C++ разраба заставить писать код на определённом подмножестве C++ (в первом приближении: не выделять память руками, не юзать указатели, не кастовать типы), то в принципе течь и падать там будет негде. На деле чуть тоньше и проще: указатели можно, но если тебе его передали в конструктор. Есть циклические ссылки, но тоже можно чё-то придумать. В общем, чёткого набора рецептов нет, опытный разраб в конкретном проекте выработает свои достаточно рабочие, плюс статический анализ и прочие там sanity-check тулзы и valgrind-ы скажут где насрано автоматически.

А есть просто Rust, где достаточно запретить писать unsafe и всё будет гарантированно блестяще и даже думать не надо.

Но говорят, Rust сложнее. Думать там надо уже просто чтобы базово взлететь, тогда как для базового взлёта на C++ достаточно быть тупорылым сишником, которому запретили выделять память. Гонят?

! ! ! ААААА ПРОСЬБА ПЕРЕНЕСТИ В TALKS ОШИБСЯ ФОРУМОМ ! ! !

Дополнение к уже написанному в треде.

  1. Тяжёлое наследие плюсов: его нет, если его не юзать. Я же не пишу в Rust ассемблерные вставки везде. Возможность их написать же не говорит о том, что у Rust тяжёлое наследие всей x86/ARM аппаратной платформы. Нормальный C++ код не содержит никаких макросов, например и передач указателей в пределе. Передай ты std::span, std::string_view и т.п. вместо (char* ptr, uint32_t size).

  2. Никогда не понимал тезис, что синтаксис обычного Си - сложный. Он может быть сложный во всех ВОЗМОЖНОСТЯХ, но в базовых сценариях он кажется примитивным: int function_name(int a, int b) { return a * b;} - это же предельно тупейшая идея синтаксиса, которую придумает любой школьник при наличии задачи изобретения ЯП. Даже конфиги хочется писать в таком стиле - см nginx. Так вот, если сознательно не усложнять себе жизнь, то C++ так же прост.

  3. Посмотрим на такие конструкции Rust, выдернутые из контекста:

languages.get_statistics(&input, &cli.ignored_directories(), &config);

Имеются какие-то &. Не знаю что это, но почему не написано input вместо &input? То есть, юзера заставляют думать про разные виды передачи аргумнетов чтоли? Ссылочно/указательно? Чем это отличается от необходимости в крестах думать про rvalue, lvalue, reference, pointer? То есть, от этого момента язык тоже не ушёл: нельзя как в JS/Python херануть объект в аргумент и зашибись - надо думать как херануть.

(0..10).map(|_| "#").collect::<String>()

Питонячно. Какой-то генератор с вызовом какой-то лямбды на каждый объект генератора? Не в курсе как это точно работает, но питонячно! В современных плюсах подобное тоже выразимо, но это уже всё равно не уровень начинающего: понимание подобного что в плюсах, что в расте - признак не дебила.

fn main() -> Result<(), Box<dyn Error>> {

В С++ проще. Не надо писать fn, чтобы сказать, что это функция, достаточно привычных миру () и тип возвращаемого значения в C++ необязательно предварять -> чтобы сообщить компилятору, какое оно. Ту же мы видим некие генерики/шаблоны - в плюсах они выглядят так же.

let mut is_sorted = false;

В Rust это выглядит НАДЁЖНЕЕ чем в С++, потому что заставили написать mut, чтобы сообщить, что это можно менять. В C++ это выглядит так же коротко в принципе: auto is_sorted = false;. Но в крестах ты пишешь const auto is_sorted = false; если надо конст и всё.

Посмотрим как пилятся сруктуры в расте:

pub struct CodeStats {
    /// The blank lines in the blob.
    pub blanks: usize,
    /// The lines of code in the blob.
    pub code: usize,
    /// The lines of comments in the blob.
    pub comments: usize,

Блин, в C++ же проще:

struct CodeStats {
  // The blank lines in the blob.
  usize blanks;
  // The lines of code in the blob.
  usize code;
  // The lines of comments in the blob.
  usize comments;

Я потратил меньше кода в крестах. Мне не надо писать pub напротив каждого поля, я могу его вынести в начало. Плюс, struct в крестах - это по-дефолту всё pub, а class - по-дефолту всё private - можно регулировать приватовость всех полей сразу просто выбором слова, которым объявлять структуру.

Что бесит в Rust: тип в конце. Но в языках, где важна производительность, люди любят подумать про memory layout - «как всё лежит в памяти» и посмотреть в первую очередь на типы всего, что лежит в структуре: какой тип рядом с каким, как это выровняется, например. Понятно, что в структуру в таких случаях данные пихают не по выравниванию, а «что рядом с чем потребляется процом», чтобы «нужное вместе» в одну кеш-линию, поэтому важнее будут имена полей, чем типы. Но всё-таки хочется «от общего к частному»: сначала видеть ЧТО ЭТО В ПРИНЦИПЕ (какой у этого тип), а уже потом как оно называется. Условно, мне хочется в «семантике общения» ситуацию «это собака, её зовут Вася и это тоже собака, и гоша», а не наоборот: «это Вася, а ещё он собака, а это петя и он собака». Я не хочу думать про имена, я хочу сначала схватывать суть уровня «так, тут у нас две псины, что они тут делают», а как они называются я потом разберусь)

pub fn summarise(&self) -> Self {

Об этом уже говорилось, в C++ тут будет меньше кода. А где тип аргумента, статически типизированные вы наши, йопт? В целом понятно, почему они заставили писать pub перед каждой функцией - чтобы тупорылый разраб сразу видел точно публичное оно или нет. А то в C++ напишут слово public: а дальше ряд функций и могут случайно написать функцию не в той секции и она случайно будет public – лучше пусть явно пишут! Но хз, это вкусовщина: мне приятнее организовывать всё именно как в плюсах: написать public и дальше у нас красиво пошёл публичный интерфейс. Нафиг мне pub в глаза пихать на каждый чих. Та же конструкция в C++: Self summarise(T &self) {. Опять же, уже говорилось: наличие & - раст оказался не таким уж простым, юзеру надо думать ссылка там или не ссылка? Где такая же простота, как в ссаном JS, что просто self написал и всё?

Давайте просто сюда посмотрим: https://github.com/sharkdp/fd/blob/master/src/filesystem.rs – в принципе да, всё читаемо, красиво, выразительно. map всякие там. Отсутствие скобок у if бесит конечно, ну ладно, в питонячке так же. Но в целом код на современных крестах выглядит абсолютно так же, может чуть меньше символов напечатать придётся. А где-то вместо map().blabla().bubu() будет несколько процедурных строк, потому что в именно стандартную библиотеку C++ не подвезли именно такой семантики, но она достижима в самом языке.

Посмотрим сюда: https://github.com/XAMPPRocky/tokei/blob/master/src/input.rs

Спецсимвольный perl-адок какой-то немного. Зачем так жить. Степень жести в районе строк 16-26 вообще ничем не способна привлечь юзера в сравнении с самыми мерзкими местами C++. Ясно, что это всё можно как-то объяснить. Точно так же «как-то» можно объяснить вот такой C++ код: https://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/src/c++11/thread.cc#L235 - синтаксис обычный такой «сишный», просто вперемешку с макросами и большим количеством ___ в названиях переменных: в общем, оба фаната «как-то» объяснят ад в своих языках, простой мане, которая хотела «простой язык» оба этих места одинаково жопные.

Хочется обратить внимание на такой тонкий психо-нюанс, который похоже имеет место быть в расте: раст далеко не питонячка, всмысле в нём уже таки надо думать о неком таком критическом количестве вещей, что требования к мозгу кандидата поднимаются на такой уровень, где ему уже совсем не противен C++ и возможные дедлоки в тредах не кажутся чем-то сложным: ой, опять мьютексы не в том порядке захватил, поправил и забыл.



Последнее исправление: lesopilorama (всего исправлений: 4)

Если C++ разраба заставить писать код на определённом подмножестве C++ (в первом приближении: не выделять память руками, не юзать указатели, не кастовать типы), то в принципе течь и падать там будет негде.

Неправда, GC нет, течь и падать очень даже может и без С указателей.

А есть просто Rust, где достаточно запретить писать unsafe и всё будет гарантированно блестяще и даже думать не надо.

Логические ошибки в коде можно накуралесить везде. Больше скажу - течка памяти в расте это фича, «потому что безопасно». У ребят голова сломана.

Но говорят, Rust сложнее.

Действительно сложнее. В расте очень много наворочено в синтаксисе и подразумевается, что ты пробуешь язык после того, как испытал много разных.

Bfgeshka ★★★★★
()

С C++ не уместно сравнивать Rust. У последнего на логотипе рак, которого переехало автомобилем и поэтому он такой каличный. Уместнее его сравнивать с другим раком 21 века — с Go, потому как это языки с похожей концепцией и ЦА в виде школьников, но Rust не такой простой. Go легко выучить и так же легко забыть, а Rust скрывает за собой целый пласт воинствующей контр-культуры с юношеским мксимализмом и неприятии всего старого, неуемным желанием переписать на нем то, что и так работает… Те этот язык знаменит лишь неадекватностью своей фан-базы. Ну а если рассматривать сам язык, то он удивляет с самого начала… У нас print это не функция, а макрос…

Мы должны писать только так:

fn main() {
    let message = "Hello, World!";
    println!("{}", message);
}

А так ошибка:

fn main() {
    let message = "Hello, World!";
    println!(message);
}

Хотя так можно:

fn main() {
    println!("Hello, World!");
}

Ну даже обычный хелллоуворлд таит загадки… Дальше которых честно говоря я и не вижу смысла копать

rtxtxtrx ★★
()
Последнее исправление: rtxtxtrx (всего исправлений: 1)

А есть просто Rust, где достаточно запретить писать unsafe и всё будет гарантированно блестяще и даже думать не надо.

Меня всегда удивляли люди, которые думают, что достаточно что-то запретить, и всё будет зае@#$сь. Причем не только в программировании, а вообще во всех областях деятельности. Говно не перестанет быть говном, если ему запретить быть коричневого цвета.

rupert ★★★★★
()

Если сравнивать именно с C++ со всеми этими его бесконечными стандартами и костылями, то Rust точно не сложнее.

И вообще мне кажется всё эти растовые концепции сложны лишь, когда мы уже в голове имеем устоявшиеся другие концепции из других языков.

При изучении с нуля обнаружиться, как минимум, более дружественный компилятор раста и начать программировать новичку возможно даже окажется легче.

unDEFER ★★★★★
()
Ответ на: комментарий от unDEFER

Согласен, Rust с ежемесячным обновлением стандартов и обновлением+устареванием крейтов на манер npm намного проще, и синтаксис как в питоне

macro_rules! forward_to_deserialize_any_method {
    ($func:ident<$l:tt, $v:ident>($($arg:ident : $ty:ty),*)) => {
        fn $func<$v>(self, $($arg: $ty,)* visitor: $v) -> 
        $crate::__private::Result<$v::Value, <Self as $crate::de::Deserializer<$l>>::Error>
        where $v: $crate::de::Visitor<$l>, {$(let _ = $arg;)*
            self.deserialize_any(visitor)}
    };
}

fn try_extract_error_from_region_constraints<'a, 'tcx>(
    infcx: &'a InferCtxt<'tcx>,
    generic_param_scope: LocalDefId,
    placeholder_region: ty::Region<'tcx>,
    error_region: Option<ty::Region<'tcx>>,
    region_constraints: &RegionConstraintData<'tcx>,
    mut region_var_origin: impl FnMut(RegionVid) -> RegionVariableOrigin,
    mut universe_of_region: impl FnMut(RegionVid) -> UniverseIndex,
) -> Option<Diag<'a>> {
    let placeholder_universe = match placeholder_region.kind() {
        ty::RePlaceholder(p) => p.universe,
        ty::ReVar(vid) => universe_of_region(vid),
        _ => ty::UniverseIndex::ROOT,
    };
    let matches =
        |a_region: Region<'tcx>, b_region: Region<'tcx>| match (a_region.kind(), b_region.kind()) {
            (RePlaceholder(a_p), RePlaceholder(b_p)) => a_p.bound == b_p.bound,
            _ => a_region == b_region,
        };
}
Жаль не все поймут красоты раста, многие ведь начнут изучать программирование с С, и навсегда повредят свой мозг его ужасным синтаксисом.

MOPKOBKA ★★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 1)

В каждой подобной теме раздувают из безобидных мелочей проблему вселенского масштаба - ай-яй-яй, память, касты, БОЖЕ, нулевые указатели!!!

Вы чего начитались одну методичку? Если такая малая ерунда, которая элементарно детектируется и исправляется вызывает такую боль, то как вы будете писать что-то действительно сложное? Как вы осилите что-нибудь многопоточное? Где нужно модифицировать что-нибудь под несколькими мьютексами, что бы все это было атомарно и не наговнокодить dead lock’ов при этом? Одна логическая ошибка, и вся твоя мегасофтина превращается в тыкву со странным поведением.

На своем мегарасте ты легко и жидко обделаешься на подобных вещах

kvpfs_2
()
Ответ на: комментарий от MOPKOBKA
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

typedef struct {
    char *tokens;
    size_t pos;
} Parser;

typedef enum {
    NUMBER,
    ADD,
    SUB,
    MUL,
    DIV
} ExprType;

typedef struct {
    ExprType type;
    union {
        double number;
        struct {
            struct Expr *left;
            struct Expr *right;
        } binary;
    } value;
} Expr;

Expr *new_number(double number) {
    Expr *expr = (Expr *)malloc(sizeof(Expr));
    expr->type = NUMBER;
    expr->value.number = number;
    return expr;
}

Expr *new_binary(ExprType type, Expr *left, Expr *right) {
    Expr *expr = (Expr *)malloc(sizeof(Expr));
    expr->type = type;
    expr->value.binary.left = left;
    expr->value.binary.right = right;
    return expr;
}

void free_expr(Expr *expr) {
    if (expr == NULL) return;
    if (expr->type != NUMBER) {
        free_expr(expr->value.binary.left);
        free_expr(expr->value.binary.right);
    }
    free(expr);
}

double eval(Expr *expr) {
    if (expr->type == NUMBER) {
        return expr->value.number;
    } else if (expr->type == ADD) {
        return eval(expr->value.binary.left) + eval(expr->value.binary.right);
    } else if (expr->type == SUB) {
        return eval(expr->value.binary.left) - eval(expr->value.binary.right);
    } else if (expr->type == MUL) {
        return eval(expr->value.binary.left) * eval(expr->value.binary.right);
    } else if (expr->type == DIV) {
        return eval(expr->value.binary.left) / eval(expr->value.binary.right);
    }
    return 0.0;
}

Parser *new_parser(const char *input) {
    Parser *parser = (Parser *)malloc(sizeof(Parser));
    parser->tokens = strdup(input);
    parser->pos = 0;
    return parser;
}

void free_parser(Parser *parser) {
    free(parser->tokens);
    free(parser);
}

char peek(Parser *parser) {
    return parser->tokens[parser->pos];
}

void consume(Parser *parser) {
    parser->pos++;
}

Expr *parse_number(Parser *parser) {
    char num_str[20];
    int i = 0;
    while (isdigit(peek(parser)) || peek(parser) == '.') {
        num_str[i++] = peek(parser);
        consume(parser);
    }
    num_str[i] = '\0';
    return new_number(atof(num_str));
}

Expr *parse_factor(Parser *parser) {
    if (isdigit(peek(parser))) {
        return parse_number(parser);
    } else if (peek(parser) == '(') {
        consume(parser);
        Expr *expr = parse_expr(parser);
        if (peek(parser) != ')') {
            fprintf(stderr, "Expected ')'\n");
            exit(1);
        }
        consume(parser);
        return expr;
    }
    fprintf(stderr, "Expected number or '('\n");
    exit(1);
}

Expr *parse_term(Parser *parser) {
    Expr *expr = parse_factor(parser);
    while (peek(parser) == '*' || peek(parser) == '/') {
        char op = peek(parser);
        consume(parser);
        Expr *right = parse_factor(parser);
        if (op == '*') {
            expr = new_binary(MUL, expr, right);
        } else {
            expr = new_binary(DIV, expr, right);
        }
    }
    return expr;
}

Expr *parse_expr(Parser *parser) {
    Expr *expr = parse_term(parser);
    while (peek(parser) == '+' || peek(parser) == '-') {
        char op = peek(parser);
        consume(parser);
        Expr *right = parse_term(parser);
        if (op == '+') {
            expr = new_binary(ADD, expr, right);
        } else {
            expr = new_binary(SUB, expr, right);
        }
    }
    return expr;
}

int main(int argc, char *argv[]) {
    if (argc != 2) {
        fprintf(stderr, "Usage: %s <expression>\n", argv[0]);
        return 1;
    }

    Parser *parser = new_parser(argv[1]);
    Expr *expr = parse_expr(parser);
    printf("%s = %f\n", argv[1], eval(expr));

    free_expr(expr);
    free_parser(parser);
    return 0;
}

vs

use std::env;
use std::str::FromStr;

#[derive(Debug)]
enum Expr {
    Number(f64),
    Add(Box<Expr>, Box<Expr>),
    Sub(Box<Expr>, Box<Expr>),
    Mul(Box<Expr>, Box<Expr>),
    Div(Box<Expr>, Box<Expr>),
}

impl Expr {
    fn eval(&self) -> f64 {
        match self {
            Expr::Number(n) => *n,
            Expr::Add(a, b) => a.eval() + b.eval(),
            Expr::Sub(a, b) => a.eval() - b.eval(),
            Expr::Mul(a, b) => a.eval() * b.eval(),
            Expr::Div(a, b) => a.eval() / b.eval(),
        }
    }
}

struct Parser {
    tokens: Vec<char>,
    pos: usize,
}

impl Parser {
    fn new(input: &str) -> Self {
        Parser {
            tokens: input.chars().collect(),
            pos: 0,
        }
    }

    fn peek(&self) -> Option<char> {
        self.tokens.get(self.pos).copied()
    }

    fn consume(&mut self) -> Option<char> {
        let token = self.peek();
        if token.is_some() {
            self.pos += 1;
        }
        token
    }

    fn parse_number(&mut self) -> Expr {
        let mut num_str = String::new();
        while let Some(ch) = self.peek() {
            if ch.is_digit(10) || ch == '.' {
                num_str.push(ch);
                self.consume();
            } else {
                break;
            }
        }
        let num = f64::from_str(&num_str).unwrap();
        Expr::Number(num)
    }

    fn parse_factor(&mut self) -> Expr {
        if let Some(ch) = self.peek() {
            if ch.is_digit(10) {
                return self.parse_number();
            } else if ch == '(' {
                self.consume();
                let expr = self.parse_expr();
                if self.consume() != Some(')') {
                    panic!("Expected ')'");
                }
                return expr;
            }
        }
        panic!("Expected number or '('");
    }

    fn parse_term(&mut self) -> Expr {
        let mut expr = self.parse_factor();
        while let Some(ch) = self.peek() {
            match ch {
                '*' => {
                    self.consume();
                    expr = Expr::Mul(Box::new(expr), Box::new(self.parse_factor()));
                }
                '/' => {
                    self.consume();
                    expr = Expr::Div(Box::new(expr), Box::new(self.parse_factor()));
                }
                _ => break,
            }
        }
        expr
    }

    fn parse_expr(&mut self) -> Expr {
        let mut expr = self.parse_term();
        while let Some(ch) = self.peek() {
            match ch {
                '+' => {
                    self.consume();
                    expr = Expr::Add(Box::new(expr), Box::new(self.parse_term()));
                }
                '-' => {
                    self.consume();
                    expr = Expr::Sub(Box::new(expr), Box::new(self.parse_term()));
                }
                _ => break,
            }
        }
        expr
    }
}

fn main() {
    let args: Vec<String> = env::args().collect();
    if args.len() != 2 {
        eprintln!("Usage: {} <expression>", args[0]);
        std::process::exit(1);
    }

    let input = &args[1];
    let mut parser = Parser::new(input);
    let expr = parser.parse_expr();
    println!("{} = {}", input, expr.eval());
}

Они что-то обои ужасны…

rtxtxtrx ★★
()
Ответ на: комментарий от unDEFER

Ну зачем же сразу с макросов начинать?

Я тоже подумал что так пример будет нечестный, именно поэтому большая его часть это не макрос а кусочек функции.

Так-то в C встроенный ассемблер есть в том числе. Он сильно проще приведëнного кода?

В gcc есть, ну он намного проще конечно, команды там простые, сложно в нем только знать, как писать так что бы было быстро.

MOPKOBKA ★★★★★
()
Ответ на: комментарий от MOPKOBKA

Да вы не стесняйтесь, давайте приведëм пример божественного всеми понятного Си:

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include "jpeglib.h"
#define J(x) jpeg_##x;
#define A B(p=t+Z*b+y*S;l=w*h;for(u=j=0;l&&j<b;p++)X->c[j\
++]=(p[w*b+h*S]-p[w*b]-p[h*S]+*p+l/2)/l,-),u+=z*z; X->d=u;}
#define G(h,n) D.comp_info[0].h##_samp_factor=1;D.image_##n;
#define B(e,f) {int x,Z=X->x,y=X->y,w=X->w,h=X->h;e;for(;h--\
;y++)for(x=Z;x<w+Z;x++)for(j=0;j<b;j++)z=d[y*s+x*b+j]f X->c[j]
#define H(W,H,w,h,B,i) for(B=z=i=0;++z<w;u>B?B=u,i=z:0)for(u=j\
=0;p=t+x*b+y*S+j,j<b;j++)K=p[z*W+h*H]-p[z*W],Q=N->c[j]*h,L=p[w*\
W+h*H]-p[w*W]-K-Q*(w-z),K-=p[h*H]-*p+Q*z,u+=L<0?-L:L,u+=K<0?-K:K;
#define W(r,X,a,o,Y,b,c,S) {S J(error_mgr E)S J(c##_##S D);D.err=\
J(std_error(&E))if(!(F=fopen(*++V,#r)))break;J(create_##c(I=&D))J(\
stdio_##o(I,F))Y;J(start_##c X)while((y=D.a##_scanline)<h)R=d+y*s,J\
(b##_scanlines(I,O=&R,1))J(finish_##c(I))J(destroy_##c(I))fclose(F);}
int main(int c,char**V){while(c==4){intptr_t w,h,s,b,S,j,i,x,y,v,q,z,k
,l,g=JCS_RGB,n=atoi(*++V);uint8_t*d;void*O,*R,*F,*I;int64_t*p,e,f,u,K,L
,*t,Q,U=1;struct{uint16_t x,y,w,h;uint8_t c[4];int64_t d;}*P,*N,*X,o={0}
;W(rb,(I);w=D.output_width;h=D.output_height;b=D.output_components;L=n=n
<1?1:n>w*h?w*h:n;K=S=(s=w*b)+b;K=sizeof(o)*L+(u=K*h+K)*9;if((L=(size_t)K
)-K||!(t=malloc(K)))break;X=P=O=t+u;d=O=P+n,output,src,J(read_header(I,U
))D.do_fancy_upsampling=0;D.out_color_space=g,read,decompress,struct)fo\
r(i=j=0;j<b;j++)for(x=j-S,z=j-b;x<S*h;x+=b)t[x+S]=x<0?y=0:t[x]+(u=y--?u+
d[z+=b]:(y=w,0));;o.w=w;o.h=h;*X=o;A for(;N=X=P,U&&++i<n;){for(U=j=0;j++
<i;X++)U=X->d>U?(N=X)->d:U;;o=*N;v=o.w;q=o.h;x=o.x;y=o.y;H(b,S,v,q,e,k)H
(S,b,q,v,f,l)e<f?o.y+=N->h=l,o.h-=l:(o.x+=N->w=k,o.w-=k);*X=o;A X=N;A}f\
or(;i--;X++)B(,=);}W(wb,(I,1),next,dest,D.input_components=b;D.in_color\
_space=g;J(set_defaults(I))G(h,width=w)G(v,height=h)J(set_quality(I,100,
D.optimize_coding=1)),write,compress,struct)free(t);return 0;}return 1;}
unDEFER ★★★★★
()

Если C++ разраба заставить писать код на определённом подмножестве C++ (в первом приближении: не выделять память руками, не юзать указатели, не кастовать типы), то в принципе течь и падать там будет негде.

Но пока что этого ни у кого не получилось.

Но говорят, Rust сложнее. Думать там надо уже просто чтобы базово взлететь, тогда как для базового взлёта на C++ достаточно быть тупорылым сишником, которому запретили выделять память. Гонят?

Мне кажется, гонят. Во-первых сишники не тупорылые, а очень умные. Во-вторых C++ мне кажется на порядок сложней Rust.

vbr ★★★★
()
Ответ на: комментарий от MOPKOBKA

Нельзя решить проблему с многопоточностью, потому как проблемы от неправильной логики. Просто не осилил правильно модифицировать данные под несколькими мьютексами, и вот у тебя операция в общем происходит неатомарно, там нет никаких race condition, но работать правильно уже не будет. Многопоток вообще сложнее на порядок всех этих детских обзывалок про текущую память

kvpfs_2
()

на сколько знаю раст разрабатывался программистами мозилы которые писали на С и С++ и которые захотели упростить себе разработку, появился набор правил которых они придерживались, их оформили в логику кода написав на окамле первый раст компилятор. Так что, по идее раст должен быть тупо проще в базовых сценариях применения.

Cergoo
()

Если C++ разраба заставить чёткого набора рецептов нет, опытный разраб в конкретном проекте выработает свои

В изначальном посте уже добрая половина ответа. Можешь считать что это очень строгий C++. И да, собственно поэтому он сложнее. Но в то же время он сильно проще C++, потому что нет многолетних наслоений.

В принципе на расте тоже можно безмозгло писать используя повсюду Rc/RefCell.

Unsafe это уже мем: создавался для удобства и структурирования, как бы с мыслью что большая часть будет будет safe, но всеми подряд используется для критики раста, что, о боже мой, в нём есть unsafe! Доходит до безумия, когда неопытные растаманы закидывают какашками других растаманов, кто много пишет unsafe, мол неправильно используете язык.

neumond
()
Ответ на: комментарий от vbr

Ну хорошо. Под багами понимается такая идиотия:

#include <iostream>

int main() {
    int x;  // Переменная x не инициализирована
    std::cout << x << std::endl;  // Неопределенное поведение
    return 0;
}

Тот же код на go:

package main

import "fmt"

func main() {
    var x int  // Переменная x не инициализирована
    fmt.Println(x)  // Ошибка компиляции: x declared and not used
}

Почему rust гарантирует «отсутствие багов», а go нет?

rtxtxtrx ★★
()
Ответ на: комментарий от rtxtxtrx

Мне выдать список?)

Да вот простая вроде бы задача: программа, в ней 10 потоков работают каждый в своем модуле (один там по сети общается, другой пляшет, третий рисует, …), общаются между собой. И тут мы хотим завершить программу, но не через KILL, а корректно, с сохранением состояния. Очевидно, что где-то имеется массив с thread’ами под мьютексом (ведь в кол-во потоков может измениться в процессе работы). Ошибкой будет наивное взятие мьютекса в пуле потоков с последующим join(), т.к. в этот момент кто-может захотеть запустить новый поток и будет dead lock. Также будет ошибкой установить флаг, который запретит запуск потоков, тк тоже может быть dead lock.

Всё сложнее, повсюду хитрые, нетривиальные решения

kvpfs_2
()
Ответ на: комментарий от rtxtxtrx

Я ничего не понял. Под багами подразумевались баги из-за гонок. Ладно, я не совсем внимательно прочитал сообщение, на которое отвечал, @kvpfs_2 в курсе, что раст гарантирует отсутствие гонок (что уже огромный плюс в сравнение со всеми остальными языками). Естественно если ты вместо плюса написал минус - такое надо тестами ловить.

А в твоих примерах аналогичная программа на Rust так же не скомпилируется, нельзя использовать неинициализированное значение.

vbr ★★★★
()
Последнее исправление: vbr (всего исправлений: 1)
Ответ на: комментарий от vbr

Естественно если ты вместо плюса написал минус

Очень просто сделать логическую ошибку в многопотоке. У меня было такое, что я с неделю только думал как всё синхронизировать и куда мьютексы понавтыкать. А гонки ловятся элементарно санитайзером. Кстати а зачем в rust завезли санитайзер, если там не надо?

kvpfs_2
()
Ответ на: комментарий от vbr

Хорошо он выводит ошибку о потенциальной гонке… И все? Только этим лучше Go? В go можно выполнить go run -race и тогда код свалится при гонке… Это, конечно, не высирание ошибки на этапе коНпеляции, но разве это безопасность? Что-то глубинное есть, что перевернет мир?

Это ж какая-то хрень:

fn main() {
    let r;
    {
        let x = 5;
        r = &x;  // Ошибка компиляции: `x` does not live long enough
    }
    println!("{}", r);
}

Тупо гарантия, что мусор не окажется…

segmentation fault (core dumped)

fn main() {
    let some_value: Option<i32> = Some(42);
    let none_value: Option<i32> = None;

    match some_value {
        Some(value) => println!("Value: {}", value),
        None => println!("No value"),
    }

    match none_value {
        Some(value) => println!("Value: {}", value),
        None => println!("No value"),
    }
}
rtxtxtrx ★★
()
Последнее исправление: rtxtxtrx (всего исправлений: 1)

Я сегодня прочитал про раст дальше примеров с хеллоуворлдами. Углубился в «безопасность». И проблемы которые решает раст: чтение мусора, нулевые указатели и состояние гонки — это дебилопроблемы совсем уж безмозглых людей. Наверное, начинающие погромизды на других языках с ними очень «часто сталкиваются», ведь все это потаенные знания о которых не пишут в книгах, статьях и туториалах… На C++ почти никто не пишет, а Rust на хайпе, поэтому он предпочтительнее для написания всякой консольной хрени — больше никаких выводов я не сделал. Нормальные ООП-языки типа C# на линуксах по факту не доступны, так что остается только этот выкидыш безопасников

rtxtxtrx ★★
()
Последнее исправление: rtxtxtrx (всего исправлений: 1)
Ответ на: комментарий от rtxtxtrx

да одна стандартная библиотека чего стоит, не говоря про всякие бусты; в чём и состоит натяжка растохейтров, чтобы показать уродливость рачта, вытаскивают кусок из недр библиотечного кода, естественно, с лайфтаймами, а такой код везде страшный и особенно в плюсах

zurg
()
Ответ на: комментарий от zurg

Ну раст объективно страшненькая, какая-то мешанина из спецсимволов даже если это типичный юзерский, небиблиотечный код. Плюсы после концептов и constexpr операций сильно похорошели, хитрые SFINAE трюки с трюхэтажными шаблонами теперь пишутся сильно симпатичнее

kvpfs_2
()
Последнее исправление: kvpfs_2 (всего исправлений: 1)
Ответ на: комментарий от rtxtxtrx

Я сегодня прочитал про раст дальше примеров с хеллоуворлдами.

молодец, уже знаешь про рачт больше большинства растохейтеров, но это всё равно очень мало, для того что бы понимать почему и зачем раст-читай дальше

И проблемы которые решает раст: чтение мусора, нулевые указатели и состояние гонки — это дебилопроблемы совсем уж безмозглых людей. Наверное, начинающие погромизды на других языках с ними очень «часто сталкиваются»,

Хех, ты не поверишь… ну и ценность раста не сводится к одной лишь безопасности

На C++ почти никто не пишет

вы там осознайте(теперь и плюсохейтеры тоже) уже, что кроме вебни и бухгалтерии в айтишечке были и есть области в которых нужны языки со строгой статической типизацией, без вариантов, и долгое время плюсы были практически безальтернативны, а так же осознайте что «и прочие там sanity-check тулзы и valgrind-ы» не заменят сделанную по науке систему типов, которая гораздо важнее чем какое-то там ООПе(и в рачте оно есть)

zurg
()
Последнее исправление: zurg (всего исправлений: 2)

Сам Rust - нормальный язык. Сообщество у него... кхм... неприятное. Ходят как свидетели Иеговы и пытаются всех обратить в свою веру. В этом истинная природа раздражения на Rust.

pathfinder ★★★★
()
Ответ на: комментарий от zurg

что кроме вебни и бухгалтерии в айтишечке были и есть области в которых нужны языки со строгой статической типизацией

Кроме вебни ничего и не осталось. А эта типизация в вебе мешает. Она не нужна. Как и не нужна скорость. Приложение на C++/C#/Rust/Go будет работать быстро, но 99% времени вхолостую, пусть лучше питон на 99% грузит проц. У него есть плюсы в виде скорости разработки и отладки, которые покрывают все недостатки. Highload пусть на чем угодно пишут. Его раз-два и обчелся.

и долгое время плюсы были практически безальтернативны

И они никому не нужны. Отечественный игропром умер в 00-х вместе с Lada Racing Club… С момента триумфа отчественного игропрома с HoM 5 прошло 20 лет

сделанную по науке систему типов, которая гораздо важнее чем какое-то там ООПе

То что ООП нет в C огромная проблема для его использования для написания чего-либо кроме говенных монолитов, те уже для GUI не пригоден. Нам нужны виджеты/компоненты которые без наследования реализовать сложно. Windows ОС потому что написана на C++, а Linux просто ядро как раз из-за C… Никаких особых типов в расте нет, он до банальности скучен. Никто на нем никогда не реализует GUI фреймворк — будут лишь одни обертки над пенсионером GTK (который «красив» лишь в примерах) и всякая электронщина

rtxtxtrx ★★
()
Ответ на: комментарий от rtxtxtrx

А эта типизация в вебе мешает. Она не нужна. Как и не нужна скорость.

Ты немного неправду сказал, потому что во времена обычного JS до появления TypeScript (тот же JS, просто статически типизированный и компилятор бьёт по рукам очень сильно) люди ели много кала с передачей в функцию объектов, у которых оказывается нет вызываемых этой функцией методов объекта или с «случайно забыл передать аргумент». JS - такой язык, в котором «всё что не напишешь, всё работает» и на этом поедено много говнины, от этого все устали, статическая типизация сильно хорошо прибралась в конюшне.

Скорость тоже нужна, потому что quake-3 в вебе запускать надо, звук генерить надо, много чо надо ввиду сильного роста мощи веб-приложений, а батарейку жрать НЕ НАДО. Поэтому выполниться вебня должна за минимум тактов проца.

lesopilorama
() автор топика
Ответ на: комментарий от MOPKOBKA

А уж какой-нибудь пропозал для рефлексии в новом стандарте С++ так вообще вершина читаемости. Любой крестовик сразу поймет, что значат всякие typename[:^char:], template for и прочие констевалы с констекспрами. Ну прямо вылитый питон.

template<typename E>
  requires std::is_enum_v<E>
constexpr std::string enum_to_string(E value) {
  std::string result = "<unnamed>";
  [:expand(std::meta::enumerators_of(^E)):] >> [&]<auto e>{
    if (value == [:e:]) {
      result = std::meta::identifier_of(e);
    }
  };
  return result;
}
// Represents a 'std::meta::info' constrained by a predicate.
template <std::meta::info Pred>
  requires (extract<bool>(substitute(^std::predicate, {type_of(Pred), ^std::meta::info})))
struct metatype {
  std::meta::info value;

  // Construction is ill-formed unless predicate is satisfied.
  consteval metatype(std::meta::info r) : value(r) {
    if (![:Pred:](r))
      throw "Reflection is not a member of this metatype";
  }

  // Cast to 'std::meta::info' allows values of this type to be spliced.
  consteval operator std::meta::info() const { return value; }

  static consteval bool check(std::meta::info r) { return [:Pred:](r); }
};

// Type representing a "failure to match" any known metatypes.
struct unmatched {
  consteval unmatched(std::meta::info) {}
  static consteval bool check(std::meta::info) { return true; }
};

// Returns the given reflection "enriched" with a more descriptive type.
template <typename... Choices>
consteval std::meta::info enrich(std::meta::info r) {
  // Because we control the type, we know that the constructor taking info is
  // the first constructor. The copy/move constructors are added at the }, so
  // will be the last ones in the list.
  std::array ctors = {members_of(^Choices, std::meta::is_constructor)[0]...,
                      members_of(^unmatched, std::meta::is_constructor)[0]};
  std::array checks = {^Choices::check..., ^unmatched::check};

  for (auto [check, ctor] : std::views::zip(checks, ctors))
    if (extract<bool>(reflect_invoke(check, {reflect_value(r)})))
      return reflect_invoke(ctor, {reflect_value(r)});

  std::unreachable();
}
archie
()
Ответ на: комментарий от lesopilorama

Это вообще-то основной пропозал из нескольких конкурирующих, который с наибольшей вероятностью попадет в С++26. И зная гомитет, можно не сомневаться, что все будет сделано максимально вырвиглазно и угребищно.

archie
()
Ответ на: комментарий от lesopilorama

TypeScript был создан разработчик трупопосцаля, дельфей и шарпа специально для фанатов поледнего, так как те испытывалт ломку от отсутствия типов еще до того как в C# стало можно везде var лепить. У него специфическая аудитория, состоящая из фанатов удод нета, TS — это проект либо на Angular + ASP.Net Core либо что намного реже React. Короче для виндузятников

JS - такой язык, в котором «всё что не напишешь, всё работает» и на этом поедено много говнины, от этого все устали, статическая типизация сильно хорошо прибралась в конюшне.

Такого не бывает. Ты всегда понимаешь, что age - это число, username и password - строки, а isAuthed - тип для буллинга. Наверное, это из-за писанины на всяких JS я не могу представить как дебилопроблемы могут быть поводом для создания целого языка, и как какой-то дегенерат вместо строки может число в функцию передать. Да и TS — это изначально про трушное ООП вместо суррогата на прототипах, а как только в JS завезли нормальное ООП, то TS стал никому не нужен кроме дотнетовцев

rtxtxtrx ★★
()
Последнее исправление: rtxtxtrx (всего исправлений: 1)
Ответ на: комментарий от rtxtxtrx

Такого не бывает. Ты всегда понимаешь, что age - это число, username и password - строки, а isAuthed - тип для буллинга.

Всё сложнее. Была функция, которая принимала obj и ИНОГДА вызывала у него obj.hello(). Про неё все забыли, она глубоко в недрах. У объекта в какой-то момент пропала hello() и была заменена на что-то другое или может быть в hello() появился аргумент.

Так вот хотелось бы, чтобы компилятор мне до деплоя кода на прод сказал, что hello() нет, а ты вызываешь или ты забыл аргумент.

Или у obj был тип Skotina, а теперь туда случайно передали bool. Внутри функции, куда это передали, нет особо ничего умного - аргумент проверялся на == undefined зачем-то по историческим причинам и оба варианта работали в JS. Но это выглядит не надёжно и жопно, хочется на этапе компиляции поймать много замечаний.

lesopilorama
() автор топика
Последнее исправление: lesopilorama (всего исправлений: 2)
Ответ на: комментарий от rtxtxtrx

Непонятно про какой ты интерфейс. Главное нововведение C++ - это деструктор. Код, который САМ вызовется в конце scope – это революционно. На этом построено просто дохрена всего сущего, из этого вытекло всякое владение, управление памятью, автоматизация и т.п.

Всякие там наследования/полиморфизмы на фоне появления в языке деструктора уже сильно вторичны, хотя приятны.

lesopilorama
() автор топика
Последнее исправление: lesopilorama (всего исправлений: 3)