Live data from Hacker News

A half-hour to learn Rust (2020)

fasterthanli.me

81–86 of 86 posts

Re: A half-hour to learn Rust (2020)

#81
Helps a lot to track toolchain improvements:

- in distrib: cargo, analyzer, clippy

- others: Aquascope godbolt intellij flowistry

__________________________

https://www.infoworld.com/article/3267624/whats-new-in-the-r...

https://github.com/cognitive-engineering-lab/aquascope

https://intellij-rust.github.io/thisweek/

Re: A half-hour to learn Rust (2020)

#82
post #9

This is, more or less, how I learned Rust circa 2015 (might have been a couple years later). I quickly started to write programs like I would have in C but got completely thwarted by the borrow checker because I wanted pointers everywhere. This made me throw my hands up and leave for other languages. However, 8 years later I did eventually come to love rust after learning the One Weird Trick of using indices instead…

Can you contrast between both approaches? How can i use indices instead of pointers? Where can i learn more?

The Entity Component System (ECS) pattern seems to side-step the Rust borrow checker entirely in order to solve the following issues, all at the same time: 1.) allow a "parent" entity to have a reference to a "related" entity, 2.) allow such a reference to a "related" entity to be mutable, 3.) allow multiple "parent" entities to reference the same "related" entity, and 4.) allow the overall system to deallocate a "related" entity at any time without invalidating the state of all the observing "parent" entities.

I have never written a game before, so Catherine West[1] might have very good reasons for choosing ECS, but I... am not so crazy about it. ECS seems to replace Rust references (raw pointers) and/or Rust smart pointers with indexes into one, large "registry" (a container: e.g., a `Vec`) of entities (e.g., `struct` instances). In other words, instead of allowing a Rust pointer (a managed memory address) to keep a piece of memory alive, ECS chooses to have a "registry" keep a piece of memory alive under a particular index, managing allocation and deallocation manually.

In a sense, ECS dumps Rust memory management in favor of... writing the good, ol', data-and-function -oriented C. Quite needlessly (?), since the same (?) can probably be accomplished with Rust reference counted pointers[2], weak pointers[3] and interior mutability[4].

----- CUT HERE -----

  use std::rc::{Rc, Weak};
  use std::cell::RefCell;
  
  type WeakMutEntity = Weak>;
  
  struct Entity {
    related: WeakMutEntity,
  }
  
  impl Entity {
    fn new(related: WeakMutEntity) -> Self {
      Self { related }
    }
  
    fn use_related(&mut self) {
      let Some(related) = self.related.upgrade() else { return; };
      related.borrow_mut().use_related();
    }
  }
  
  let entity1 = Rc::new(RefCell::new(
    Entity::new(/* null */ Weak::new())));
  
  let entity2 = Rc::new(RefCell::new(
    Entity::new(Rc::downgrade(&entity1))));
  
  entity2.borrow_mut().use_related();
  
----- CUT HERE -----

If the above does not get the job done, the solution shouldn't be to just abandon the Rust borrow checker; the solution should be to get the Rust Gods to optimize the available pointer types syntax-wise and/or performance-wise.

[1] https://www.youtube.com/watch?v=aKLntZcp27M

[2] https://doc.rust-lang.org/book/ch15-04-rc.html

[3] https://doc.rust-lang.org/book/ch15-06-reference-cycles.html

[4] https://doc.rust-lang.org/book/ch15-05-interior-mutability.h...

Re: A half-hour to learn Rust (2020)

#83
post #75

The YouTube channel "No Boilerplate" turned this into a 10-minute video version, for anyone that prefers video: https://www.youtube.com/watch?v=br3GIIQeefY

(no boilerplate author here) Thank you so much for linking! When I was first getting started I learned so much from Amos and his INSANE deep-dives over at fasterthanli.me, basically he taught me Rust. For my 3rd video I messaged him and asked if I could base it on his article and he said 'go for it' - since then we've both found ways to go full-time in our respective worlds, and we shitpost in a private discord toget…

Your videos (and by extension you) are awesome and have been one of the biggest motivations for me learning Rust and continuing programming. Thank you.

Re: A half-hour to learn Rust (2020)

#84

Earlier quoted context omitted.

Isn't that one weird trick more or less defeating the whole purpose of rust's ownership/borrowing model by moving the problems one level up the ladder? Having seen that kind of opinion stated elsewhere, it seems what most people would like is rust minus borrowing, and I feel I would get behind that too.

> what most people would like is rust minus borrowing It's already possible. Use Rust reference-counted smart pointers[1] for shareable immutable references and internal mutability[2] for non-shareable mutable references checked at runtime instead of compile time. [1] https://doc.rust-lang.org/book/ch15-04-rc.html [2] https://doc.rust-lang.org/book/ch15-05-interior-mutability.h...

I think this is one of the main things Rust users don’t promote enough (I don’t see this in the article for instance).

let lock = Arc::new(RwLock::new(1));

This plus a few unwrapping things is enough to get you almost std::shared_ptr and then you can fight the borrow checker some other day.

Re: A half-hour to learn Rust (2020)

#85

Earlier quoted context omitted.

> what most people would like is rust minus borrowing It's already possible. Use Rust reference-counted smart pointers[1] for shareable immutable references and internal mutability[2] for non-shareable mutable references checked at runtime instead of compile time. [1] https://doc.rust-lang.org/book/ch15-04-rc.html [2] https://doc.rust-lang.org/book/ch15-05-interior-mutability.h...

I think this is one of the main things Rust users don’t promote enough (I don’t see this in the article for instance). let lock = Arc::new(RwLock::new(1)); This plus a few unwrapping things is enough to get you almost std::shared_ptr and then you can fight the borrow checker some other day.

> this is one of the main things Rust users don’t promote enough

This is probably because using internal mutability not to achieve a valid design goal (such as a controlled side-effect on an otherwise immutable entity), but to side-step the borrow checker for ease of programming, is not considered idiomatic Rust and even though it makes a good educational tool, it should rather not end up in production codebases.

Firstly, when using regular references, you cannot create multiple mutable references. This rule prevents subtle bugs such as data races. When using internal mutability, you still keep that protection, but it is (undesirably) delayed from compile time to runtime, potentially causing an unavoidable crash/panic.

Secondly, when using regular references, you cannot create even a single mutable reference when an immutable reference already exists. This rule prevents subtle bugs such as unexpected mutation ("from under you") of data that was passed-in as immutable. When using internal mutability, you throw away that protection, since multiple immutable reference owners can request a mutable reference (even if only one at a time).

  use std::rc::Rc;
  use std::cell::RefCell;
  
  let entity1 = Rc::new(RefCell::new(42));
  let /* immutable */ entity2 = entity1.clone();
    
  *entity1.borrow_mut() += 27;
  assert_ne!(*entity2.borrow_mut(), 42);

Re: A half-hour to learn Rust (2020)

#86

Earlier quoted context omitted.

Can you contrast between both approaches? How can i use indices instead of pointers? Where can i learn more?

The Entity Component System (ECS) pattern seems to side-step the Rust borrow checker entirely in order to solve the following issues, all at the same time: 1.) allow a "parent" entity to have a reference to a "related" entity, 2.) allow such a reference to a "related" entity to be mutable, 3.) allow multiple "parent" entities to reference the same "related" entity, and 4.) allow the overall system to deallocate a "re…

The key benefits of an ECS system with large static arrays of data is (a) to avoid the speed overhead of managing memory allocation and deallocation - instead of doing it automatically or manually, these memory allocations/deallocations never happen during operations, allocated just once at startup and deallocated all at once; (b) avoid the memory overhead of having to store any metadata per each item, as your basic unit of allocation is "all items of this kind" and, very importantly, (c) ensure memory locality, that all the consecutive items are always in continuous memory in a cache-friendly manner, as you're going to repeatedly iterate over all of them.

No construction made up of any kind of pointers can achieve that, unless there's a Sufficiently Smart compiler that can magically fully eliminate these pointers.

Post reply on HN