Wrong compiler error?

Hello,

I was doing the scrabble score exercise from Exercism and I noticed that the error the compiler was giving wasn't really the error it should be giving.

Consider the following code:

pub fn score(word: &str) -> u64 {
    word.chars()
        .map(|c| {
            match c.make_ascii_uppercase() {
                'A' => 1, 
                _ => 0,
            }
        }).sum()
}

The compiler gives me the following error:

error[E0308]: mismatched types
 --> src/lib.rs:6:17
  |
5 |             match c.make_ascii_uppercase() {
  |                   ------------------------ this expression has type `()`
6 |                 'A' => 1, 
  |                 ^^^ expected `()`, found `char`

error: aborting due to previous error

but the actual error is that c is immutable and make_ascii_uppercase() takes &mut self.

I consider that the compiler is giving me the wrong error, am I correct in assuming that?

Nope; the compiler is doing type checking before mutability checking. As you stated, char::make_ascii_uppercase takes the char by mutable reference, and does not return it. Therefore, the function returns () and that's where the compiler gets mad, and stops compiling.

2 Likes

Since my previous post was a bit rushed, I'll explain a bit more:

let c = 'a';
// Here's your code
let x = match c.make_ascii_uppercase() {
    'A' => 1,
    _ => 0,
};
x

This desugars into:

let c = 'a';
let temp = &mut c;
let temp_2 = char::make_ascii_uppercase(temp);
let x = match temp_2 {
    'A' => 1,
    _ => 0,
}
x

The error comes from temp_2 being a unit (), and the match on it expecting a char. Should we change it to be

let x = match temp_2 {
    () => 1,
    _ => 0,
}

Then you would get an error on the mutability of c.


Also, please note that if your code really only makes this check, then you could change it to be:

c.make_ascii_uppercase();
let x = c == 'A';
x as u64

Since true maps to 1, and false maps to 0. However, the compiler can probably already optimize this anyway.

1 Like

Hmm, it still doesn't make 100% sense to me. How come that the immutable version returns a () instead of a char if the mutability doesn't come into play if "it hasn't been checked"?

In other words

let temp_2 = char::make_ascii_uppercase(temp);

How did the compiler determine the type of temp_2 without checking the mutability? It looks to me that mutability did come into play, since changing the mutability apparently changes the type.

Because the char::make_ascii_uppercase function returns ().

Ok, that makes sense to me, but then the desugared code doesn't anymore.

let temp = &mut c;
let temp_2 = char::make_ascii_uppercase(temp);
let x = match temp_2 { ... }

Why are we matching on temp_2 instead of temp?

I think I figured it out.

1 Like

If instead of

match c.make_ascii_uppercase() {
	'A' => 1, 
	_ => 0,
}

I write

c.make_ascii_uppercase();
match c {
	'A' => 1, 
	_ => 0,
}

then the compiler behaves as I expect it to. I guess I had a wrong assumption about make_ascii_uppercase().

I just realized I was looking for to_ascii_uppercase instead of make_ascii_uppercase :smiley:

2 Likes