I was looking at the Rust reference book and it states that a tuple struct is "refutable when one of its subpatterns is refutable". It also states that identifier patterns are irrefutable when "the subpattern is not specified".
Given this, if I create a tuple struct pattern with a single irrefutable identifier subpattern, I would expect the tuple struct pattern to be irrefutable as well because its subpatterns are all irrefutable.
pub fn main() {
enum Foo {
Bar(i32),
Baz(i32),
}
// Compiler says Foo::Bar(x) is a refutable pattern
// but the reference book suggests it is irrefutable
let Foo::Bar(x) = Foo::Bar(50);
}
The AST of the code above confirms that the Foo::Bar(x) is a tuple struct pattern and the x is an identifier pattern. I feel that the compiler is correct here, but the reference book suggests the Foo::Bar(x) is irrefutable despite the compiler.
I understand the need for blocking refutable patterns given the multiple possible enum variants and how an if let or similar would help here but am confused by the contradicting quotes.
Is something going on here that I am missing, or is this an issue with the reference book?
The type of Foo::Bar(50) is Foo, just as the type of a 4 is i32.
Look at this:
let 4 = 4;
error[E0005]: refutable pattern in local binding: `i32::MIN..=3_i32` and `5_i32..=i32::MAX` not covered
--> src/main.rs:2:9
|
2 | let 4 = 4;
| ^ patterns `i32::MIN..=3_i32` and `5_i32..=i32::MAX` not covered
|
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
= note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
= note: the matched value is of type `i32`
This is refutable because the type of the 4 on the tright is i32. The value is irrelevant for refutability. It's all about the type.
And let 4 = <i32> is refutable, because <i32> could be a value different than 4.
I think refutability only talks about types, not about values.
Of course I'm not a theoretical language expert, so I might be completely off. But that's my understanding of what refutability means.
The refutability section of the Reference appears underspecified for enums (you'll see there's also no information on struct variants of enum types). The missing piece here is that the enum variant name is also a subpattern, and in an enum with two or more constructible variants, it is refutable:
// refutable
// |
// | irrefutable (with respect to variant `Bar`)
// | |
let Foo::Bar(x) = Foo::Bar(50);
I would provide a reference, but unfortunately the refutability section appears underspecified for enums... (etc.)
Related
I have some code like this
struct MyStruct {
id: u32
}
fn main() {
let vec: Vec<MyStruct> = vec![MyStruct {
id: 1
}];
let my_struct = vec[0];
}
I thought my_struct must be of reference type, but the compiler proved I was wrong:
error[E0507]: cannot move out of index of `Vec<MyStruct>`
--> src/main.rs:10:21
|
10 | let my_struct = vec[0];
| ^^^^^^ move occurs because value has type `MyStruct`, which does not implement the `Copy` trait
|
help: consider borrowing here
|
10 | let my_struct = &vec[0];
So I read the doc of trait Index and noticed the sentence below:
container[index] is actually syntactic sugar for *container.index(index)
It's counterintuitive. Why rust dereferences it? Does it mean that it's more friendly to types those impl Copy?
There are plenty of other questions asking about this (1 2 3) but none really address the why part.
Rust has made many design decisions work to make ownership clear. For example, you cannot pass a value, v, directly to a function f that expects a reference. It must be f(&v). There are places where this isn't always the case (macros like println! and auto-ref for method calls being prime exceptions), but many parts of the language follow this principle.
The behavior of the index operator is pretty much the same as the normal field access operator .. If you have my_struct: &MyStruct then my_struct.id will yield a u32 and not a &u32. The default behavior is to move fields. You have to introduce a & (i.e. &my_struct.id) to get a reference to the field. So the same is with the index operator. If you want to make it clear that you want only a reference to the element, then you need to introduce a &.
I ran the following example in the standard library documentation, and there was a puzzle.
I found an implementation of the BorrowMut trait with Vec,
I don't understand how it works. For example, where the code below indicates that No.1 works, why doesn't No.2 work, what does the generic T do?
use std::borrow::BorrowMut;
fn check<T: BorrowMut<[i32]>>(mut v: T) {
assert_eq!(&mut [1, 2, 3], v.borrow_mut()); // ! no.1 can call, Why?
}
fn main() {
let v = vec![1, 2, 3];
// v.borrow_mut(); // ! no.2 Can't call,Why?
check(v);
}
The full error shown by rustc explains it pretty well. With emphasis added:
error[E0283]: type annotations needed
--> src/main.rs:9:7
|
9 | v.borrow_mut(); //! no.2 Can't call,Why?
| --^^^^^^^^^^--
| | |
| | cannot infer type for type parameter `Borrowed` declared on the trait `BorrowMut`
| this method call resolves to `&mut Borrowed`
| help: use the fully qualified path for the potential candidate: `<Vec<T> as BorrowMut<[T]>>::borrow_mut(v)`
|
= note: cannot satisfy `Vec<i32>: BorrowMut<_>`
BorrowMut can be implemented multiple times on a type. For example, both BorrowMut<Foo> and BorrowMut<Bar> could be implemented for Vec<i32>; in those examples, Foo and Bar take the place of the "type parameter Borrowed declared on the trait BorrowMut", as shown in its documentation (click "Show declaration").
In "no.1" you have specified that T: BorrowMut<[i32]>; since no other implementations of BorrowMut for T have been mentioned, the "type parameter Borrowed declared on the trait BorrowMut" can unambiguously be inferred to be [i32].
In "no.2", there is ambiguity over which implementation of BorrowMut you're after: even if no other implementations of BorrowMut for Vec<i32> are in scope right now, they could be lurking somewhere the compiler doesn't know about (pending being brought in-scope with a use statement); and even if no others exist right now, an upstream crate (namely, the std library) could add one in the future—and that would then break your code. Therefore the compiler asks you to remove the ambiguity by explicitly informing it which implementation you're after. It does this by reporting "type annotations needed", and even shows you how: "use the fully qualified path for the potential candidate: <Vec<T> as BorrowMut<[T]>>::borrow_mut(v)". We can do that here:
<Vec<i32> as BorrowMut<[i32]>>::borrow_mut(v)
However this won't work for a separate reason: Rust only performs deref coercion when calling with the . method syntax—when calling like this instead, you'd have to explicitly pass &mut v instead of v (I've filed an issue about this erroneous suggestion); and that still won't work in your case because v was not declared mutable.
The compiler also concluded with:
For more information about an error, try `rustc --explain E0283`.
Doing that would have displayed this extra information, which may also have helped.
Do please suggest how the error message could have explained the problem more clearly.
In actual fact, the compiler's suggestion above is more verbose than it requires to resolve the ambiguity in this case. You could also have resolved it without mentioning the type of v in either of these ways:
<dyn BorrowMut<[i32]>>::borrow_mut(&mut v)
BorrowMut::<[i32]>::borrow_mut(&mut v)
Also note that, in the case of Vec, you can obtain a mutable slice of its content (which is what this implementation of BorrowMut provides for you) via its intrinsic as_mut_slice method, indexing (via its implementation of std::ops::IndexMut), or calling upon std::ops::DerefMut::deref_mut whether explicitly or implicitly (i.e. using the dereference operator). Respectively:
v.as_mut_slice()
&mut v[..]
v.deref_mut() // DerefMut must be in scope, else qualify as above
&mut *v
Indeed, because of the dereferencing, &mut Vec also coerces to a mutable slice at coercion sites such as function arguments and let bindings—so you can often just use &mut v and Rust will do the rest for you.
See all approaches discussed in this answer on the Rust Playground.
Given that they all compile down to exactly the same code, which you use is really just a matter of preference. But, in order to keep the code as clear (and therefore maintainable) as possible, I would suggest that the indexing approach &mut v[..] most clearly and concisely indicates that you're taking a mutable slice over the whole of the vector.
Please consider the following minimal example in Rust:
const FOOBAR: usize = 3;
trait Foo {
const BAR: usize;
}
struct Fubar();
impl Foo for Fubar {
const BAR: usize = 3;
}
struct Baz<T>(T);
trait Qux {
fn print_bar();
}
impl<T: Foo> Qux for Baz<T> {
fn print_bar() {
println!("bar: {}", T::BAR); // works
println!("{:?}", [T::BAR; 3]); // works
println!("{:?}", [1; FOOBAR]); // works
println!("{:?}", [1; T::BAR]); // this gives an error
}
}
fn main() {
Baz::<Fubar>::print_bar();
}
The compiler gives the following error:
error[E0599]: no associated item named `BAR` found for type `T` in the current scope
--> src/main.rs:24:30
|
24 | println!("{:?}", [1; T::BAR]); // this gives an error
| ^^^^^^ associated item not found in `T`
|
= help: items from traits can only be used if the trait is implemented and in scope
= note: the following trait defines an item `BAR`, perhaps you need to implement it:
candidate #1: `Foo`
Whatever the answer to my question, this is not a particularly good error message because it suggests that T does implement Foo despite the latter being a trait bound. Only after burning a lot of time did it occur to me that in fact T::BAR is a perfectly valid expression in other contexts, just not as a length parameter to an array.
What are the rules that govern what kind of expressions can go there? Because arrays are Sized, I completely understand that the length are to be known at compile time. Coming from C++ myself, I would expect some restriction akin to constexpr but I have not come across that in the documentation where it just says
A fixed-size array, denoted [T; N], for the element type, T, and the non-negative compile-time constant size, N.
As of Rust 1.24.1, the array length basically needs to either be a numeric literal or a "regular" constant that is a usize. There's a small amount of constant evaluation that exists today, but it's more-or-less limited to basic math.
a perfectly valid expression in other contexts, just not as a length parameter to an array
Array lengths don't support generic parameters. (#43408)
this is not a particularly good error message
Error message should be improved for associated consts in array lengths (#44168)
I would expect some restriction akin to constexpr
This is essentially the restriction, the problem is that what is allowed to be used in a const is highly restricted at the moment. Notably, these aren't allowed:
functions (except to construct enums or structs)
loops
multiple statements / blocks
Work on good constant / compile-time evaluation is still ongoing. There are a large amount of RFCs, issues, and PRs improving this. A sample:
Const fn tracking issue (RFC 911)
Allow locals and destructuring in const fn (RFC 2341)
Allow if and match in constants (RFC 2342)
I have a tiny playground example here
fn main() {
let l = Some(3);
match &l {
None => {}
Some(_x) => {} // x is of type &i32
}
}
I'm pattern matching on &Option and if I use Some(x) as a branch, why is x of type &i32?
The type of the expression &l you match against is &Option<i32>, so if we are strict the patterns should be &None and &Some(x), and if we use these patterns, the type of x indeed is i32. If we omit the ampersand in the patterns, as you did in your code, it first looks like the patterns should not be able to match at all, and the compiler should throw an error similar to "expected Option, found reference", and indeed this is what the compiler did before Rust version 1.26.
Current versions of Rust support "match ergonomics" introduced by RFC 2005, and matching a reference to an enum against a pattern without the ampersand is now allowed. In general, if your match expression is only a reference, you can't move any members out of the enum, so matching a reference against Some(x) is equivalent to matching against the pattern &Some(ref x), i.e. x becomes a reference to the inner value of the Option. In your particular case, the inner value is an i32, which is Copy, so you would be allowed to match against &Some(x) and get an i32, but this is not possible for general types.
The idea of the RFC is to make it easier to get the ampersands and refs in patterns right, but I'm not completely convinced whether the new rules actually simplified things, or whether they added to the confusion by making things magically work in some cases, thereby making it more difficult for people to get a true understanding of the underlying logic. (This opinion is controversial – see the comments.)
I have two dummy functions:
fn foo() -> u32 { 3 }
fn bar() -> u32 { 7 }
And I want to create a boxed slice of function pointer: Box<[fn() -> u32]>. I want to do it with the box syntax (I know that it's not necessary for two elements, but my real use case is different).
I tried several things (Playground):
// Version A
let b = box [foo, bar] as Box<[_]>;
// Version B
let tmp = [foo, bar];
let b = box tmp as Box<[_]>;
// Version C
let b = Box::new([foo, bar]) as Box<[_]>;
Version B and C work fine (C won't work for me though, as it uses Box::new), but Version A errors:
error[E0605]: non-primitive cast: `std::boxed::Box<[fn() -> u32; 2]>` as `std::boxed::Box<[fn() -> u32 {foo}]>`
--> src/main.rs:8:13
|
8 | let b = box [foo, bar] as Box<[_]>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait
Apparently, for some reason, in the version A, the compiler isn't able to coerce the function items to function pointers. Why is that? And why does it work with the additional temporary let binding?
This question was inspired by this other question. I wondered why vec![foo, bar] errors, but [foo, bar] works fine. I looked at the definition of vec![] and found this part which confused me.
This looks like an idiosyncracy of the type inference algorithm to me, and there probably is no deeper reason for this except that the current inference algorithm happens to behave like it does. There is no formal specification of when type inference works and when it doesn't. If you encounter a situation that the type inference engine cannot handle, you need to add type annotations, or rewrite the code in a way that the compiler can infer the types correctly, and that is exactly what you need to do here.
Each function in Rust has its own individual function item type, which cannot be directly named by syntax, but is diplayed as e.g. fn() -> u32 {foo} in error messages. There is a special coercion that converts function item types with identical signatures to the corresponding function pointer type if they occur in different arms of a match, in different branches of an if or in different elements of an array. This coercion is different than other coercions, since it does not only occur in explicitly typed context ("coercion sites"), and this special treatment is the likely cause for this idiosyncracy.
The special coercion is triggered by the binding
let tmp = [foo, bar];
so the type of tmp is completely determined as [fn() -> u32; 2]. However, it appears the special coercion is not triggered early enough in the type inference algorithm when writing
let b = box [foo, bar] as Box<[_]>;
The compiler first assumes the item type of an array is the type of its first element, and apparently when trying to determine what _ denotes here, the compiler still hasn't updated this notion – according to the error message, _ is inferred to mean fn() -> u32 {foo} here. Interestingly, the compiler has already correctly inferred the full type of box [foo, bar] when printing the error message, so the behaviour is indeed rather weird. A full explanation can only be given when looking at the compiler sources in detail.
Rust's type solver engine often can't handle situations it should theoretically be able to solve. Niko Matsakis' chalk engine is meant to provide a general solution for all these cases at some point in the future, but I don't know what the status and the timeline of that project is.
[T; N] to [T] is an unsizing coercion.
CoerceUnsized<Pointer<U>> for Pointer<T> where T: Unsize<U> is
implemented for all pointer types (including smart pointers like Box
and Rc). Unsize is only implemented automatically, and enables the
following transformations:
[T; n] => [T]
These coercions only happen at certain coercion sites:
Coercions occur at a coercion site. Any location that is explicitly
typed will cause a coercion to its type. If inference is necessary,
the coercion will not be performed. Exhaustively, the coercion sites
for an expression e to type U are:
let statements, statics, and consts: let x: U = e
Arguments to functions: takes_a_U(e)
Any expression that will be returned: fn foo() -> U { e }
Struct literals: Foo { some_u: e }
Array literals: let x: [U; 10] = [e, ..]
Tuple literals: let x: (U, ..) = (e, ..)
The last expression in a block: let x: U = { ..; e }
Your case B is a let statement, your case C is a function argument. Your case A is not covered.
Going on pure instinct, I'd point out that box is an unstable magic keyword, so it's possible that it's just half-implemented. Maybe it should have coercions applied to the argument but no one has needed it and thus it was never supported.