Chocrates

Chocrates

Hands-on Rust: Chapter 7 Invalid Camera Method from Chapter 5

In the end of chapter 7 we write the movement function in movement.rs
We call the camera on_player_move method like so:

camera​.on_player_move​(&want_move.destination);

This results in a compile error on my machine because on_player_move expects a reference.

The latest code in the book I see for the camera method is back in Chapter 5

 	    ​pub​ ​fn​ ​on_player_move​(&​mut​ ​self​, player_position: Point) {
​ 	        ​self​.left_x = player_position.x ​-​ DISPLAY_WIDTH​/​2;
​ 	        ​self​.right_x = player_position.x + DISPLAY_WIDTH​/​2;
​ 	        ​self​.top_y = player_position.y ​-​ DISPLAY_HEIGHT​/​2;
​ 	        ​self​.bottom_y = player_position.y + DISPLAY_HEIGHT​/​2;
​ 	    }

Looking at the code zip it appears that the function is always expecting a reference, though I got a bit lost on the folder names so it could be wrong there too.

Changing the method to expect a reference fixes the compile error and it appears to run fine.

I didn’t test it but I think the method could take in either a Point or an &Point and as long as the method is called the same it should be fine? If this is true, is this because destination always is a calculated value that doesn’t need to worry about the borrow checker?

Marked As Solved

herbert

herbert

Author of Hands-on Rust

Thanks for finding that - great catch! I’m working on clearing that one up now, so it should make beta 2. It looks like an error slipped in when I merged some code branches on my machine (the bundled source compiles because the on_player_move signature changed without being mentioned in the text).

It’ll work either way, so long as the function is either &Point or Point and the calling code follows the same convention. The correct form is just Point. The reasoning behind this is a little complicated (more so than I can justify adding into a beginner’s book), so I’ll try and explain it.

The Point structure (source) has a few properties:

  • It’s internal representation is two i32, 32-bit signed integers.
  • It derives Copy to make it “trivially copyable”.
  • It implements all manner of niceties to work with other libraries and use a crate named Ultraviolet under the hood for fast SIMD math when you use it as a (math-style) vector.

Since the type is two 32-bit integers, its memory representation is 64-bits long. That’s really helpful, because on 64-bit architectures (most PCs these days) it fits into a single register. Copying it to a function becomes a VERY fast operation - place it in a register and off it goes. Even without a register, copying a single 64-bit value is a ridiculously fast memcpy operation under the hood. The fun part is that sending a reference is actually slower in this case! References are really pointers under the hood. So the system sends a pointer to the value to the function. The pointer is 64-bits long, so the pointer is the exact same size. But the function then has to de-reference the pointer - lookup the memory address being pointed to, and get the value from there. So the computer has to perform an extra step to get to the value. There’s a Clippy warning in “pedantic” mode that will warn you about this sometimes.

When you compile in release mode, LLVM (the underlying compiler system) is often smart enough to transform a read-only pointer into a copy. It’s also smart enough to realize that if it puts the value into a register earlier and “inlines” the function call (literally copy it inline into the calling code) it can skip copying/passing altogether. It doesn’t always get that right, but it usually does. (“RVO - Return Value Optimization” can cause “copy elision” to happen, often eliminating the entire copy. It’s not guaranteed to happen in Rust, but it usually does)

So, that’s great - but there are times you want to be referencing Point rather than copying it. Every time you are getting one out of Legion, a reference is preferable because you are operating on the original value in Legion’s data store. If you are updating the point (with an &mut) you absolutely have to use a reference - otherwise your updates won’t apply to the original. When you are accessing a bunch of Point values, you tend to gain more from them being adjacent in cache than you lose from the pointer jump - a jump to something in L1 cache is effectively instant.

Hope that wasn’t too long an explanation. TLDR: it’s fixed for beta 2, thank you for spotting it.

Also Liked

Chocrates

Chocrates

Thanks that helps.

Would a function ever declare a mut parameter instead of &mut? Is that even possible or would ever make sense?
Feel free to tell me to pound sand if you don’t want to answer those types of questions here :slight_smile:

herbert

herbert

Author of Hands-on Rust

That’s a fun one. :slight_smile: I’ve written a few functions with mutable, non-reference parameters - usually when porting directly from C++ and its ilk. It’s a good way to let the function modify the parameter without side-effects - that is, the changes to the parameter never leave the function.

Say you had a function:

fn do_stuff(mut n: i32) {
   while n > 0 {
      // Do something with n here!
      n -= 1;
   }
}

When you call do_stuff, it modifies n inside the function - but the original n that is passed in never changes. n is either a copy or a move depending upon the type - but it is now local to the function, just as if you’d typed let mut n = 5 as the first line of the function. So when you call it:

let mut n = 5;
do_stuff(n);
println!("{}", n);

You still get the output of 5, because n is copied (i32 copies by default - most primitives do). Whereas if you’d use a mutable reference, you’d get:

fn do_stuff(n: &mut i32) {
   while n > 0 {
      // Do something with n here!
      n -= 1;
   }
}

..
let mut n = 5;
do_stuff(&mut n);
println!("{}", n);

You will get the output 0, because the original n was modified.

I often think of mutable borrow parameters as “out” parameters (some other languages use that terminology) - because they are effectively returning something, even if they are doing it through a parameter rather than a return statement.

Hope that helps! (Sorry about the edits; I wrote it on my phone the first time, and Rust isn’t well suited to mobile keyboards)

Where Next?

Popular Pragmatic Bookshelf topics Top

mikecargal
Title: Hands-On Rust (Chap 8 (Adding a Heads Up Display) It looks like ​.with_simple_console_no_bg​(SCREEN_WIDTH*2, SCREEN_HEIGHT*2...
New
joepstender
The generated iex result below should list products instead of product for the metadata. (page 67) iex> product = %Product{} %Pento....
New
jeremyhuiskamp
Title: Web Development with Clojure, Third Edition, vB17.0 (p9) The create table guestbook syntax suggested doesn’t seem to be accepted ...
New
jskubick
I think I might have found a problem involving SwitchCompat, thumbTint, and trackTint. As entered, the SwitchCompat changes color to hol...
New
oaklandgit
Hi, I completed chapter 6 but am getting the following error when running: thread 'main' panicked at 'Failed to load texture: IoError(O...
New
rainforest
Hi, I’ve got a question about the implementation of PubSub when using a Phoenix.Socket.Transport behaviour rather than channels. Before ...
New
EdBorn
Title: Agile Web Development with Rails 7: (page 70) I am running windows 11 pro with rails 7.0.3 and ruby 3.1.2p20 (2022-04-12 revision...
New
New
roadbike
From page 13: On Python 3.7, you can install the libraries with pip by running these commands inside a Python venv using Visual Studio ...
New
dachristenson
I’ve got to the end of Ch. 11, and the app runs, with all tabs displaying what they should – at first. After switching around between St...
New

Other popular topics Top

DevotionGeo
I know that these benchmarks might not be the exact picture of real-world scenario, but still I expect a Rust web framework performing a ...
New
brentjanderson
Bought the Moonlander mechanical keyboard. Cherry Brown MX switches. Arms and wrists have been hurting enough that it’s time I did someth...
New
New
AstonJ
Just done a fresh install of macOS Big Sur and on installing Erlang I am getting: asdf install erlang 23.1.2 Configure failed. checking ...
New
DevotionGeo
The V Programming Language Simple language for building maintainable programs V is already mentioned couple of times in the forum, but I...
New
First poster: bot
zig/http.zig at 7cf2cbb33ef34c1d211135f56d30fe23b6cacd42 · ziglang/zig. General-purpose programming language and toolchain for maintaini...
New
PragmaticBookshelf
Build modern server-driven web applications using htmx. Whatever programming language you use, you’ll write less (and cleaner) code. ...
New
PragmaticBookshelf
Use advanced functional programming principles, practical Domain-Driven Design techniques, and production-ready Elixir code to build scal...
New
PragmaticBookshelf
Quickly turn complex problems into simple, working solutions using functional programming, safe concurrency, and the expressive tools of ...
New
xiji2646-netizen
There’s a GitHub repo at forrestchang/andrej-karpathy-skills that’s sitting at 97.8k stars. It’s a single CLAUDE.md file with four behavi...
New

Sub Categories: