8

I like many things the rust language does but my god sometimes it looks just horrendously ugly and is awful to read

Comments
  • 4
    If it was difficult to write, it should also be difficult to read because that's only fair.
  • 3
    @Fast-Nop I dont even mean the difficult to write stuff

    Even simple things tend to just look.... awful.

    And all thats without macros, my god the macro syntax
  • 1
    Major reason why I don't use it.
  • 2
    @ElectroArchiver I want to like the language, it does many things really well
    But some design decisions are just beyond me

    Like how the return statement is completely optional
  • 0
    @uNrEaL-jAsE 🪦
  • 1
    @uNrEaL-jAsE I do like C#, but I would say it has quite different use cases to rust

    Edit: also I would heavily restrain myself on the newer features of it (think C# 9 and newer). Some of these are genuinely awful
  • 0
    @LotsOfCaffeine what do you mean by return is optional?

    Any block of logic always returns something
  • 3
    I like it. Everything is right there in the code, no ambiguity, and the syntax is easy to generate with macros. In fact, my main grievances about rust are things that can't be explicitly specified, such as the interaction between local variable scopes in async functions and await calls that decides whether the async state machine is ultimately Send or not.
  • 0
    Getting rid of the weird and limiting distinction between a statement and an expression was long overdue.
  • 0
    @thebiochemic I mean syntactically, you dont need to use the return keyword.

    fn increment(a: i32) -> i32 {
    return a + 1;
    }

    Is the same as

    fn increment(a: i32) -> i32 {
    a + 1
    }
  • 1
    @LotsOfCaffeine and why is that bad?

    Not trying to be rude, i'm just trying to understand your reasoning.

    The reason it is that way is for the simple fact, that these are fundamentally two different things:

    the return keyword returns and exits a function explicitly, while the statement without a semicolon only returns a value to its parent block, which may or may not be the same thing, if it's at the end of the function.

    If you don't use anything at all, it implicitly returns (). That also applies to blocks.

    if you ask me, that's really clever.
  • 1
    @LotsOfCaffeine It works really well with functional-style programming which is something Rust explicitly supports.
  • 0
    @thebiochemic @lorentz
    I think its horrendous to have both

    It creates this weird situation where I'm trying to read a block of code and have to remind myself that this one line without the semicolon is actually a return statement

    Implicitness is something rust tends to avoid in many situations, and I can support that. But I dont get why they do it here. I'm not against functional programming I just wish it wouldn't look so ugly
  • 0
    @LotsOfCaffeine that is not true, the whole type system (and lifetimes system) of rust is built in a way, that you don't need to explicitly say, what type something is (or how long it lives), unless it can't be inferred from the code.
  • 1
    @LotsOfCaffeine I never thought of this as implicitness, I just associate the semicolon with a value being discarded unless the given line is a let binding.
  • 1
    @thebiochemic when it comes to types yes, but I was thinking about implicit conversion for example - which doesn't exist in rust.

    @lorentz interesting, for me the semicolon just closes a statement, and if I don't see one it immediately looks off to me. But then again I've mostly programmed in languages with C-like syntax.
  • 1
    @LotsOfCaffeine I think it's more confusing when switching from C because you're more prone to think in statements, which is somethng Rust doesn't really have, or rather, only as a special type of expression.
  • 1
    Okay I was just programming some rust and I have another complaint:

    When passing a function as a parameter, there is

    fn

    Fn

    FnMut

    FnOnce

    especially fn and Fn

    who thought it'd be a good idea to have two things which are fairly similar, but very much distinct, with such similar names?
  • 1
    @LotsOfCaffeine
    fn is the raw data, that has a lifetime and a scope.
    Fn is the data, that can be owned (similar to &str vs String)

    Though now that you mention it, i'm not entirely sure, what differentiates Fn from FnOnce and FnMut, i need to look it up myself.
    But i would assume, that FnOnce is taking data and consumes it, while FnMut takes data and mutates it within it's scope. All of them have their use cases, but i agree, that they are confusing.

    The other thing, that you mentioned with implicit conversion is very much a thing and is called coercion, but it has some strict rules, when something is coerced into something else.
  • 1
    @LotsOfCaffeine It's a stupid way to name them from the outside but it makes some semblance of sense in context. I would've named fn rawFn or fnptr or cfn personally.

    Basically, fn is a primitive, like i32 or usize or bool or char. If a type is lowercase you know it's a primitive (or the author is an ass.) It represents pure C function pointers which are often compiled to a single memory address. Fn, FnOnce and FnMut are traits implemented by the ad-hoc structs defined to associate closures with the data they capture from the immediately enclosing scope. The nature of these ad-hoc structs is best understood by trying to rewrite closures into closure-free code, there's pretty much one way to do it and that is exactly what compilers do.
  • 2
    We need all four of these, fn has to be lowercase while the others are uppercase, and Fn, FnMut and FnOnce have to be similar because they're members of the same inheritance chain. The only mistake I see is that if the traits adopt "Fn" to mean callable, the actual type fn should somehow indicate that it's literally a function.
  • 2
    Actually I'm not sure whether fn is a primitive. It is in the sense that it has an invariant small size, but it could also be treated as

    - a complex type because it has type params (what's more, a variable number of them!)

    - a type system element like struct, tuple or enum because it's a core element in algebraic type systems associated with the logical "implies" operation. (fn(T) -> U means "if we have a T, we can get a U")

    A counterargument to the last option that points towards the complex type interpretation would be that assignability works completely differently for struct, enum and for fn, tuple. Maybe fn and touple could be honorary complex types?
  • 1
    @lorentz it is a bit confusing
    I get that they're different types, they are slightly different to each other after all, its just the naming that caught me off guard

    I just wanted to pass a callback to a function, I'm now using FnOnce for that matter
Add Comment