Apparent unused variable in match statement - rust

I am implementing a simple library system to keep track of my pdfs.
I have a Subject enum and a Entry struct defined as follows:
pub enum Subject {
Math,
Programming,
CompSci,
Language,
Misc,
None
}
pub struct Entry {
pub subject: Subject
}
I am trying to implement a function that will operate on a vector of Entry's and return a Vec<&Entry> whose entries match a given Subject.
I have a simple Library struct that is a wrapper around a Vec<Entry>:
pub struct Library {
pub entries: Vec<Entry>
}
In order to do so, I need to iterate through entries and filter only the elements whose .subject field correspond to the desired subject. To accomplish this I have created a function that will return a predicate function.
Here is the get_subject function:
impl Library {
pub fn get_subject(&self, subject: Subject) -> Vec<&Entry> {
let pred = subject_pred(subject);
self.entries.iter().filter(pred).collect::<Vec<&Entry>>()
}
}
which calls the function subject_pred to create the correct predicate function:
// Return a PREDICATE that returns true when
// the passed ENTRY matches the desired SUBJECT
fn subject_pred(subject_UNUSED: Subject) -> impl FnMut(&&Entry) -> bool {
|e: &&Entry| if matches!(&e.subject, subject_UNUSED) {
true
} else {
false
}
}
Here's the problem. This syntax compiles just fine but apparently the subject_UNUSED local variable in subject_pred is "unused". I am flabbergasted as my syntax clearly shows intent to match with the passed subject_UNUSED. When I test out this function on a vector of entries, the predicate always returns true (hence why I am receiving the "unused" warning) but I have literally no idea why.
If anyone could explain why the match statement is always matched, that would be greatly appreciated. I tried using a regular match statement but the same warning is popping up, and this is not the behavior that I am trying to code. If I don't include the subject_UNUSED in a traditional match statement, the compiler tells me that I have to cover the Math, Programming, CompSci, Language, Misc and None variants of my enum, which indicates to me that everything up until that point is good.

You cannot match against a variable. What you've done is equivalent to
matches!(&e.subject, some_subject)
That matches any Subject, just like a wildcard (_), except it also captures it in the some_subject variable (can be used in a guard like matches!(&e.subject, subject_UNUSED if subject_UNUSED == ...)). Neither the captured variable nor the parameter (which is shadowed by it) are used.
What you need to do is to #[derive(PartialEq)] then use ==:
if e.subject == subject_UNUSED { ... }
By the way, your code also has other problems: you don't move into the closure and you're taking owned entries but produce borrowed.

Related

Is there a way to capture the parameter types of a function definition with a macro?

Ultimately, I'd like to be able to wrap a function in a proc_macro like this:
native_function! {
fn sum(x:i32, y:i32) -> i32 {
x+y
}
}
I'm trying to find what I need to put in this function:
#[proc_macro]
pub fn native_function(item: TokenStream) -> TokenStream {
...
}
Inside the function above, I need to be able to capture the types of the parameters defined in the function; in this case - "i32"
I've tried all sorts of ways to do this, but currently I'm stumped. I'm trying to avoid using a derive macro since I don't want to have to complete and instantiate a struct. I don't even need a full solution, but if someone can point me to the function/object/library I need to be using to that would be great.
syn is the cannonical parsing library for proc macros.
Using it this is easy:
let f = syn::parse_macro_input!(item as syn::ItemFn);
let types: Vec<syn::Type> = f
.sig
.inputs
.into_iter()
.filter_map(|arg| match arg {
syn::FnArg::Receiver(_) => None,
syn::FnArg::Typed(syn::PatType { ty, .. }) => Some(*ty),
})
.collect();
By the way, your macro can be an attribute macro.
If you want to know whethere the type is a some known type, note first that you can never be sure; that is because macros operate without type information, and code like struct i32; is legal and will shadow the primitive i32 type.
If you've accepted this limitation, what you actually want is to compare it to a path type. Path types are represented by the Path variant of syn::Type. It has qself, which is the T in <T as Trait>::AssociatedType and None in a simple path (not fully-qualified, just A::B::C), and path, which is the Trait::AssociatedType or A::B::C. Paths are complex; they can contain generics, for example, but if all you want is to check if this is one-segment type of simple identifier like i32, syn has got you covered: just use Path::is_ident(). So:
let is_i32 = match ty {
syn::Type::Path(ty) => ty.path.is_ident("i32"),
_ => false,
}
If you want to compare against a more complex type, you will have to walk and match segment-by-segment.

Why do I get the error "no method named collect found for type Option"?

I'm doing the Exercism Rust problem in which a string has arbitrary length, but could be null, and needs to be classified based on its last two graphemes.
My understanding is that Option is used to account for something that could be null, or could be not null, when this is unknown at compile time, so I've tried this:
extern crate unicode_segmentation;
use unicode_segmentation::UnicodeSegmentation;
pub fn reply(message: &str) -> &str {
let message_opt: Option<[&str; 2]> = message.graphemes(true).rev().take(2).nth(0).collect();
}
My understanding of which, is that the right hand side will give an array of two &strs, if the string is non zero in length, or will return none, and the left hand side will store it as an option (so that I can later match on Some or None)
The error is:
no method named 'collect' found for type std::option::Option<&str> in the current scope
This doesn't make sense to me, as I (think) I'm trying to collect the output of an iterator, I am not collecting an option.
The error message isn't lying to you. Option does not have a method called collect.
I (think) I'm trying to collect the output of an iterator
Iterator::nth returns an Option. Option does not implement Iterator; you cannot call collect on it.
Option<[&str; 2]>
You can't do this, either:
How do I collect into an array?
I'd write this as
let mut graphemes = message.graphemes(true).fuse();
let message_opt = match (graphemes.next_back(), graphemes.next_back()) {
(Some(a), Some(b)) => Some([a, b]),
_ => None,
};

How to pattern match on values inside a type implementing Deref, such as Box, without copying the contents?

I have data contained inside a Box, and would like to pattern match on it without accidentally copying the Box's contents from the heap to the stack; how do I do that?
Let's assume the following code:
enum SomeEnum {
SomeEntry,
AnotherEntry,
}
fn main() {
let boxed_value = Box::new(SomeEnum::AnotherEntry);
match *boxed_value {
SomeEnum::SomeEntry => {}
SomeEnum::AnotherEntry => {}
}
}
Does this copy the enum out of the box onto the stack and pattern match on that copy, or does it do the matching directly on the value pointed to by the box?
What about this variant?
use std::ops::Deref;
enum SomeEnum {
SomeEntry,
AnotherEntry,
}
fn main() {
let boxed_value = Box::new(SomeEnum::AnotherEntry);
match boxed_value.deref() {
SomeEnum::SomeEntry => {}
SomeEnum::AnotherEntry => {}
}
}
It seems that simply dereferencing a box does not automatically create a copy, otherwise one would not be able to create a reference to the contained value by using let x = &*boxed_value. This leads to a question about this syntax:
enum SomeEnum {
SomeEntry,
AnotherEntry,
}
fn main() {
let boxed_value = Box::new(SomeEnum::AnotherEntry);
match &*boxed_value {
SomeEnum::SomeEntry => {}
SomeEnum::AnotherEntry => {}
}
}
First: in Rust, there are no implicit costly copies, unlike in, for example, C++. Whereas in C++, the default action is "deep copy" (via copy constructor or similar), the default action in Rust is moving. A move is a shallow copy which (a) is usually very small and cheap and (b) can be removed by the optimizer in most cases. To get deep clones in Rust you have manually use .clone(). If you don't do that, you usually don't really have to worry about this.
Second: matching on an enum only looks at the discriminant of that enum (unless you bind enum fields, see below). That's the "tag" or the "metadata" which specifies which variant of the enum is stored in a value. That tag is tiny: it fits in 8 bits in almost all cases (enums with more than 256 variants are rare). So you don't need to worry about that. And in your case, we have a C-like enum without any fields. So the enum only stores the tag and hence is tiny, too.
So what about enum fields that might be costly to copy? Like this:
enum SomeEnum {
SomeEntry(String),
AnotherEntry,
}
let boxed_value = Box::new(SomeEnum::AnotherEntry);
match *boxed_value {
SomeEnum::SomeEntry(s) => drop::<String>(s), // make sure we own the string
SomeEnum::AnotherEntry => {},
}
So in this case one variant stores a String. Since deep-copying a string is somewhat costly, Rust won't do it implicitly. In the match arm we try to drop s and assert it's a String. That means we (meaning: the body of the match arm) own the string. So, if the match arm owns it but we didn't get the owned value from cloning it, that means that the outer function doesn't own it anymore. And in fact, if you try to use boxed_value after the match, you will get move errors from the compiler. So again, either you get a compiler error or no bad things automatically happen.
Furthermore, you can write SomeEnum::SomeEntry(ref s) in the match. In that case, the string is bound by reference to s (so the drop() call won't work anymore). In that case, we never move from boxed_value. This is something I call "deferred moving", but I'm not sure if that's an official term for it. But it just means: when pattern matching, the input value is not moved at all until a binding in the pattern moves from it.
Lastly, please take a look at this code and the generated assembly. The assembly is optimal. So once again: while you might be worried about accidental clones when you come from the C++ world, this is not really something you need to worry about in Rust.

What are Some and None?

I came across some output I don't understand using Vec::get. Here's the code:
fn main() {
let command = [('G', 'H'), ('H', '5')];
for i in 0..3 {
print!(" {} ", i);
println!("{:?}", command.get(i));
}
}
the output is
0 Some(('G', 'H'))
1 Some(('H', '5'))
2 None
I've dabbled in Haskell before, and by that I mean looked at a tutorial site for 10 minutes and ran back to C++, but I remember reading something about Some and None for Haskell. I was surprised to see this here in Rust. Could someone explain why .get() returns Some or None?
The signature of get (for slices, not Vec, since you're using an array/slice) is
fn get(&self, index: usize) -> Option<&T>
That is, it returns an Option, which is an enum defined like
pub enum Option<T> {
None,
Some(T),
}
None and Some are the variants of the enum, that is, a value with type Option<T> can either be a None, or it can be a Some containing a value of type T. You can create the Option enum using the variants as well:
let foo = Some(42);
let bar = None;
This is the same as the core data Maybe a = Nothing | Just a type in Haskell; both represent an optional value, it's either there (Some/Just), or it's not (None/Nothing).
These types are often used to represent failure when there's only one possibility for why something failed, for example, .get uses Option to give type-safe bounds-checked array access: it returns None (i.e. no data) when the index is out of bounds, otherwise it returns a Some containing the requested pointer.
See also:
Why don't Option's Some and None variants need to be qualified?
What is the difference between Some and Option in Rust?
Think of Some and None as the canonical "safe" way of working around the fact that the Rust language does not support "safe" use of NULL pointers. Since the length of your Vec is 3, and you have only specified two pairs, the third pair is effectively NULL; instead of returning NULL, it returns None.
Rust provides safety guarantees by forcing us at compile-time, via Some / None, to always deal with the possibility of None being returned.
command is not a vector (type Vec<T>), it is a fixed-size array (type [(char, char); 2] in your case), and arrays are automatically borrowed into slices (views into arrays), hence you can use all methods defined on slices, including get:
Returns the element of a slice at the given index, or None if the index is out of bounds.
The behavior is pretty obvious: when given index is valid, it returns Some with the element under that index, otherwise it returns None.
There is another way to access elements in a slice - the indexing operator, which should be familiar to you:
let nums = [1, 2, 3];
let x = nums[1];
It returns the element of the slice directly, but it will fail the current task if the index is out of bounds:
fn main() {
let x = [1, 2];
for i in 0..3 {
println!("{}", x[i]);
}
}
This program fails:
% ./main2
1
2
task '<main>' failed at 'index out of bounds: the len is 2 but the index is 2', main2.rs:4
The get() method is needed for convenience; it saves you from checking in advance if the given index is valid.
If you don't know what Some and None really are and why they are needed in general, you should read the official tutorial, it explains it because it is very basic concept.
Option enum has 2 variants.
1- None is used to indicate failure or no value
2- Some which is tuple-struct that wraps the value
If you need to write this structure in OOB, for example in typescript, you would write like this. This would make it easier to visualize the situation
Define Option interface as derived class
interface Option<T = any> {
// pass all the methods here
// unwrap is used to access the wrapped value
unwrap(): T;
}
write Some class which inherits from Option
Some class returns a value
class Some<T> implements Option<T> {
private value: T;
constructor(v: T) {
this.value = v;
}
unwrap(): T {
return this.value
}}
Write None class which also inherits from Option
None class returns null
class None<T> implements Option<T> {
// you do not need constructor here
unwrap(): T {
return null as T;
}
}
The other answers discussing the return type for get() being option enum are accurate, but I think what is helpful is how to remove the some from the prints. To do that a quick way is to just call the unwrap on the option, although this is not production recommended. For a discussion on option take a look at the rust book here.
Updated with unwrap code in playground (below)
fn main() {
let command = [('G', 'H'), ('H', '5')];
for i in 0..3 {
print!(" {} ", i);
println!("{:?}", command.get(i).unwrap());
}
}

How to write a fn that processes input and returns an iterator instead of the full result?

Forgive me if this is a dumb question, but I'm new to Rust, and having a hard time writing this toy program to test my understanding.
I want a function that given a string, returns the first word in each line, as an iterator (because the input could be huge, I don't want to buffer the result as an array). Here's the program I wrote which collects the result as an array first:
fn get_first_words(input: ~str) -> ~[&str] {
return input.lines_any().filter_map(|x| x.split_str(" ").nth(0)).collect();
}
fn main() {
let s = ~"Hello World\nFoo Bar";
let words = get_words(s);
for word in words.iter() {
println!("{}", word);
}
}
Result (as expected):
Hello
Foo
How do I modify this to return an Iterator instead? I'm apparently not allowed to make Iterator<&str> the return type. If I try #Iterator<&str>, rustc says
error: The managed box syntax is being replaced by the `std::gc::Gc` and `std::rc::Rc` types. Equivalent functionality to managed trait objects will be implemented but is currently missing.
I can't figure out for the life of me how to make that work.
Similarly, trying to return ~Iterator<&str> makes rustc complain that the actual type is std::iter::FilterMap<....blah...>.
In C# this is really easy, as you simply return the result of the equivalent map call as an IEnumerable<string>. Then the callee doesn't have to know what the actual type is that's returned, it only uses methods available in the IEnumerable interface.
Is there nothing like returning an interface in Rust??
(I'm using Rust 0.10)
I believe that the equivalent of the C# example would be returning ~Iterator<&str>. This can be done, but must be written explicitly: rather than returning x, return ~x as ~Iterator<&'a str>. (By the way, your function is going to have to take &'a str rather than ~str—if you don’t know why, ask and I’ll explain.)
This is not, however, idiomatic Rust because it is needlessly inefficient. The idiomatic Rust is to list the return type explicitly. You can specify it in one place like this if you like:
use std::iter::{FilterMap, Map};
use std::str::CharSplits;
type Foo = FilterMap<'a, &'a str, &'a str,
Map<'a, &'a str, &'a str,
CharSplits<'a, char>>>
And then list Foo as the return type.
Yes, this is cumbersome. At present, there is no such thing as inferring a return type in any way. This has, however, been discussed and I believe it likely that it will come eventually in some syntax similar to fn foo<'a>(&'a str) -> Iterator<&'a str>. For now, though, there is no fancy sugar.

Resources