I kinda think rust is a dead-end. The learning curve is really challenging for most people, and you gain all these new constraints.

It competes against languages that give you memory safety by having garbage collection, or I guess even by using arenas in a few cases. GC comes with a performance hit, but a bit of a performance hit is usually much easier to deal with than having a smaller pool of effective developers

Worse is better

Nah. It's like learning vim, you get over the first shock and then you have a lifetime friend.

And another thing is with current coding agents, like the gpt-5-codex, Rust really shines here. The agent can easily guide you through the first borrow checker issues, and the compiler helps the agent to write good code.

Source: been writing Rust for a decade now. It is easily the best language I've ever used, still.

I'm experiencing that shock now. Can't create a global instance of my self-made heap datastructure. I'm longing for C memory management at the moment - it seems a tiny price to pay.

Now I learn that linked lists aren't possible and one has to use some, frankly, bullshit scheme to pretend to have them. IMO the documentation isn't really preparing people for the new reality of what can be done and how to do things.

Linked lists aren’t just possible in Rust, they are in the standard library.

They are just an example of the borrow checker’s limitations, which mean that you can only form a DAG of mutable references. But that’s why we have `unsafe`.

The way I think about rust is this: you can experience the pain up front while you’re getting your program to compile, or you can experience pain later during runtime in random unpredictable ways. Rust is the former. It takes time to learn how to reduce the up front pain, but I’m so much more confident in the end result.

I say this having years of coding background in languages like C, C#, Java, JavaScript, Python, etc.

It's fair enough but if one isn't writing multi-threaded code or code with some complicated ownership scheme then that upfront investment may never really deliver a return.

Not true. Whole classes of bugs are just eliminated - most importantly NPEs. I’ve seen tiny scripts in node that crash due to lack of an exception handler, and I’ve witnessed countless runtime errors in Java in the simplest of web services.

I've written utility programs that never bother to deallocate memory because the program needs all of it until the point where it exits anyhow.

You can do the same in Rust with Box::leak, which takes an owned pointer and gives you back a 'static borrow. The only caveat is that unless you reconstitute the type its Drop impl won't be called, but that's likely exactly what you wanted.

That’s certainly possible but I think it’s increasingly untrue for all but the smallest programs. The problem is that it’s hard to appreciate how many times your program didn’t crash but would have if you’d used a different language. You don’t need to be very complex before you have the risk of something crashing due to an edge case around values which are almost never null, even in languages like Python.

My productivity level in C is medium. In rust I'm competely blocked because I want to create a global variable and absolutely none of the described ways in the docs or stackoverflow work for my example. Should I give up on this "bad idea".....well I cannot because I can't accept not understanding why this is not working when people say it can.

Compared to C the situation is outlandishly, even hellishly impossible to understand. If I can't understand this one thing then I feel there's no point in continuing so I must stay and battle it till I get it or give up completely. I don't think I've ever hit anything like this in any other language I've ever learned.

Have you thought about just not making the variable global and instead adding it as a parameter to the functions that actually need it?

You can also create a struct, put your "global" variable inside it and then put all the functions that need the variable into an Impl block of that struct. If you then add the parameter `&self` to these functions, you can access the "global"variable any time via `self.global_variable`.

If that is not enough, then you can always make an actual global variable by first wrapping it in a Mutex, to prevent simultaneous access and then wrapping that in an Arc for Atomic Reference Counting. That allows you to pass "copies" of that variable around anywhere, satisfying the borrow-checker (since the variable is now reference-counted in a thread-safe way).

If you need a lot of parallel reading, replacing the Mutex with an RwLock is a good idea, since it allows locking from multiple threads, if you want to read it in most cases.

You've said "I want to create a global variable and absolutely none of the described ways in the docs or stackoverflow work for my example" a few times now, and I still have no idea what you are stuck on. Global variables are much the same in C and Rust, as ownership problems largely disappear for global variables.

Maybe if you provided the example, someone might provide an insight. I hate to suggest this - but have you asked an AI? They've seen a lot of code. If you give an AI enough context it will likely cough up examples of how others have solved it.

Thank you for offering to look. I hesitate to supply the example because I have tried so many things and get so many different errors depending on what approach I try to take.

I don't want to waste your time. What I'm doing now is just trying to make global variables with builtin data types like String. This takes away some of the options for error. My idea is to get this working before I try to do it with my own data structure:

So with a simpler case where I'm making a global string instead

  // ....
   static mut HEAP_INSTANCE : OnceLock<String> = OnceLock::new();

  #[rocket::main]
   async fn main() -> Result<(), rocket::Error> {

      HEAP_INSTANCE.set("blah".to_string());

      ^^^^^^^^^^^^^ use of mutable static

      let _rocket = rocket::build()
          .mount("/", routes![min,create_heap])
          .launch()
          .await?;

      Ok(())
   }

As you can see it's an error to use a static mut. I realise that this thing isn't safe in a multi-threaded program but I had hoped that I might ignore that in my single-threaded program or add some kind of locking container that would make it acceptable to the compiler.

If I try to use my heap data structure then I start having a multitude of issues, the most common being that I create something which is owned by a scope that disappears too soon. IOW I need to initialise the global with a structure that has a 'static lifetime and I'm doing that in a context which definitely isn't static. i.e. I get "creates a temporary value which is freed while still in use" or something similar

While writing this reply I might have solved my issue:

  //static mut HEAP_INSTANCE : OnceLock<String> = OnceLock::new();
  static HEAP_INSTANCE : OnceLock<Box<Heap<String>>> = OnceLock::new();

  async fn main() -> Result<(), rocket::Error> {
    let heap = Box::new(Heap::new(100));
    let _ = HEAP_INSTANCE.set(heap);
    // ....
  }

I don't fully understand why I don't need the mut and I don't know if I really need a Box or not but at least this compiles.

Thank you for helping me to duck debug! :-D

The counterpoint I'd have for this argument is that code that isn't multithreaded or with complicated ownership scheme ends up being very simple looking Rust, so the difficulty of the language doesn't come into play.

Have you tried to write a linked list as an exercise? I know it's a common beginner exercise in C and C++, but in Rust I really don't recommend implementing data structures as a beginner. While some are possible to implement safely, most require unsafe to implement at least efficiently and aren't very beginner friendly.

Rust competes with both GC languages (offering way superior performance) and with C++ (offering safety and modern language features).

It’s a pretty strong value proposition, but it doesn’t mean there aren’t good reasons to pick a GC language if you can afford the performance hit.

The list of reasons to pick C++ over Rust is very short, mostly momentum, recruitment, and legacy. For greenfield projects, the list is even shorter.

The general experience is that developing software in Rust is slightly more expensive compared with, say, C#, and way, way cheaper than C++.

I get the sense that Rust is probably a much better replacement for C++ than C, and that Rust discussions frequently devolve because C programmers and C++ programmers are talking past each other, without understanding the divide.

I strongly disagree.

* The performance difference can be pretty massive depending on scenarios. Sure, most programs don't need it, but still * Rust has not only memory safety but full safety around it. For instance, in Java if you iterate over a list while mutating it you'll get a runtime error. In Rust, it won't compile, preventing you from a possible bug later on * The time you spend learning Rust is so much time saved afterwards because the language is specifically designed to reduce the risk of programming errors. Many things like the absence of a `null` value like we have in Java is an immense time saver when your program gets bigger

Why do you take it for granted that I can't just iterate and mutate and it works just fine?

It could work, if iteration is using indices. But honestly I prefer it to be an error, as this is often the sign of a bug.

I mean plain iteration uses function calls. Iterators are a more advanced concept that needs wrapper objects and function calls.

> as this is often the sign of a bug.

Why? I've just written that yesterday, I had never a problem with that. For example after deleting an element from an array I need to commit this change, by adjusting the size, etc. Why is it so unexpected that I also need to adjust the index? The index needs to be modified anyway.

Where is the notion coming from that this is less preferable than the workarounds, like marking some elements for deletion and then iterating a second time or by making a second container and moving everything that shouldn't be deleted (.filter) ?

Exactly. You need to adjust the index, and if you don't you have a bug. But how to adjust the index depends on what you're doing.

And sometimes it's a simple bug where you never meant to modify the collection.

But I need to adjust indices for a container when modifying anyways, regardless whether it occurs in a loop. It's not different than doing not doing it in the loop. Also I'm also modifying the loop index anyways, since I want to iterate, not always touch the same object.

> And sometimes it's a simple bug where you never meant to modify the collection.

How do you accidentally modify an array, which would be different from any other variable?

But the index is not yours. The iterator keeps it.

> How do you accidentally modify an array, which would be different from any other variable?

Like you cause any other simple bug. When there are tons of things and it's not a simple variable. Or even just a typo. That also happens.

If the iterator abstracts the iteration away, it should also abstract over the modification.

> Like you cause any other simple bug.

Yes, but we don't forbid modifying other things, just because you could modify them by accident, because you want to modify them some of the time. Why does a[i] = ... needs to be prevented, but a = ... is fine?

> If the iterator abstracts the iteration away, it should also abstract over the modification.

That's no longer a simple iterator; it's a collection wrapper that represents in-iteration collection. It can be useful, and it is possible to write! But I don't think this is what programming languages should offer as their default iterator. Also, how do you solve the problem of mutation done through the collection without involving the iterator?

> Yes, but we don't forbid modifying other things, just because you could modify them by accident, because you want to modify them some of the time. Why does a[i] = ... needs to be prevented, but a = ... is fine?

I agree, this is not a strong reason on its own, but it strengthen the main reason.

> That's no longer a simple iterator; it's a collection wrapper that represents in-iteration collection.

I am not using a language with iterators in daily work, but that doesn't sound like a real problem to me. The iterator is already provided by the container type and the container already supports removing things:

    iter<T>::drop_current () {
        this->container.remove_at (this->index);
        this->index--;
    }
> Also, how do you solve the problem of mutation done through the collection without involving the iterator

Same like you solve mutation done to the container while the iterator exists, when the iterator doesn't allow mutating. That problem already exists.

I fail to see how mutating a container while iterating is more likely to be a bug, than mutating any other thing.

s/I mean plain iteration uses function calls/I mean plain iteration uses indices/

> The time you spend learning Rust is so much time saved afterwards because the language is specifically designed to reduce the risk of programming errors

[Citation needed]

> It competes against languages that give you memory safety by having garbage collection

I wouldn’t use it where I could get away with garbage collection (nor C/C++) but that still leaves enough space for Rust to exist in.

I guess I just very rarely find problems where GC isn't an option. Even the ones where people say "this has to be fast so we can't use a GC" usually work well with a GC.

Rust is much easier to learn and use than C++, its main competitor.

languages with a gc are using rust to deal with their horrific performance. look at python and javascript.

"Languages with GC" aren't just Python and JavaScript, and they're amongst the slowest language in this camp.