Why I cannot compare two `&'static str`s in a const context?

This isn't actually quite correct, as this is still a compile error:

const fn f(lhs: &'static [u8], rhs: &'static [u8]) -> bool { lhs == rhs }
error[E0277]: can't compare `[u8]` with `_` in const contexts
 --> src/lib.rs:1:66
  |
1 | const fn f(lhs: &'static [u8], rhs: &'static [u8]) -> bool { lhs == rhs }
  |                                                                  ^^ no implementation for `[u8] == _`
  |
  = help: the trait `~const PartialEq<_>` is not implemented for `[u8]`
note: the trait `PartialEq<_>` is implemented for `[u8]`, but that implementation is not `const`
 --> src/lib.rs:1:66
  |
1 | const fn f(lhs: &'static [u8], rhs: &'static [u8]) -> bool { lhs == rhs }
  |                                                                  ^^
  = note: required for `&[u8]` to implement `~const PartialEq<&_>`

(The error is clearer now than it used to be, although it's not ideal that it's referring to unstable ~const syntax rather than just saying the trait implementation cannot be used in const contexts.)

It is interesting that it works in a match, though:

const X: &[u8] = b"";
const fn f(s: &'static [u8]) -> bool {
    match s {
        X => true,
        _ => false,
    }
}

AIUI the reason this occurs is because matching on &str goes through the derive(PartialEq) implementation, whereas matching on &[_] actually becomes a slice pattern instead, and the primitive matching on both slices and u8 can be used on const contexts as they don't go through PartialEq/StructuralEq.

You can see this in effect on the playground by choosing to show MIR:

pub fn str(s: &'static str) -> bool {
    match s {
        "" => true,
        _ => false,
    }
}

pub fn u8s(s: &'static [u8]) -> bool {
    match s {
        b"" => true,
        _ => false,
    }
}
// WARNING: This output format is intended for human consumers only
// and is subject to change without notice. Knock yourself out.
// Editorialized by @CAD97
fn str(_s: &str) -> bool {
    let _return: bool;
    let _eq: bool; // tmp

    bb0: {
        _eq = <str as PartialEq>::eq(_s, const "") -> bb1;
    }

    bb1: {
        switchInt(move _eq) -> [false: bb2, otherwise: bb3];
    }

    bb2: {
        _return = const false;
        goto -> bb4;
    }

    bb3: {
        _return = const true;
        goto -> bb4;
    }

    bb4: {
        return;
    }
}

fn u8s(_s: &[u8]) -> bool {
    let _return: bool;
    let _len_s: usize;
    let _len_pat: usize;
    let _4: bool;

    bb0: {
        _len_s = Len((*_s));
        _len_pat = const 0_usize;
        _eq = Eq(move _len_s, move _len_pat);
        switchInt(move _4) -> [false: bb1, otherwise: bb2];
    }

    bb1: {
        _return = const false;
        goto -> bb3;
    }

    bb2: {
        _return = const true;
        goto -> bb3;
    }

    bb3: {
        return;
    }
}
2 Likes