rust

T.I.L.

`iter` vs `into_iter`

Tthe difference between `iter`, `into_iter` and `iter_mut` on Rust

TL;DR

  • The iterator returned by into_iter may yield any of T, &T or &mut T, depending on the context.
  • The iterator returned by iter will yield &T, by convention.
  • The iterator returned by iter_mut will yield &mut T, by convention.

The first question is: "What is into_iter?"

into_iter comes from the IntoIterator trait:

pub trait IntoIterator 
where
    <Self::IntoIter as Iterator>::Item == Self::Item, 
{
    type Item;
    type IntoIter: Iterator;
    fn into_iter(self) -> Self::IntoIter;
}

You implement this trait when you want to specify how a particular type is to be converted into an iterator. Most notably, if a type implements IntoIterator it can be used in a for loop.

For example, Vec implements IntoIterator... thrice!

impl<T> IntoIterator for Vec<T>
impl<'a, T> IntoIterator for &'a Vec<T>
impl<'a, T> IntoIterator for &'a mut Vec<T>

Each variant is slightly different.

This one consumes the Vec and its iterator yields values (T directly):

impl<T> IntoIterator for Vec<T> {
    type Item = T;
    type IntoIter = IntoIter<T>;
 
    fn into_iter(mut self) -> IntoIter<T> { /* ... */ }
}

The other two take the vector by reference (don't be fooled by the signature of into_iter(self) because self is a reference in both cases) and their iterators will produce references to the elements inside Vec.

This one yields immutable references:

impl<'a, T> IntoIterator for &'a Vec<T> {
    type Item = &'a T;
    type IntoIter = slice::Iter<'a, T>;
 
    fn into_iter(self) -> slice::Iter<'a, T> { /* ... */ }
}

While this one yields mutable references:

impl<'a, T> IntoIterator for &'a mut Vec<T> {
    type Item = &'a mut T;
    type IntoIter = slice::IterMut<'a, T>;
 
    fn into_iter(self) -> slice::IterMut<'a, T> { /* ... */ }
}

So:

What is the difference between iter and into_iter?

into_iter is a generic method to obtain an iterator, whether this iterator yields values, immutable references or mutable references is context dependent and can sometimes be surprising.

iter and iter_mut are ad-hoc methods. Their return type is therefore independent of the context, and will conventionally be iterators yielding immutable references and mutable references, respectively.

ref: https://stackoverflow.com/a/34745885

Subslice Pattern Rust

Use pattern matching on Rust slices/vectors

This is a usefull pattern on Rust match when you want to match slices of a vector.

Imagine this problem: We want to implement a function which takes an vector containing the names of people that like an item. It must return the display text as shown in the examples:

[]                                -->  "no one likes this"
["Peter"]                         -->  "Peter likes this"
["Jacob", "Alex"]                 -->  "Jacob and Alex like this"
["Max", "John", "Mark"]           -->  "Max, John and Mark like this"
["Alex", "Jacob", "Mark", "Max"]  -->  "Alex, Jacob and 2 others like this"
For 4 or more names, the number in 'and 2 others' simply increases.

We can solve this with:

fn likes(names: &[&str]) -> String {
  match names {
    [] => format!("no one likes this"),
    [a] => format!("{} likes this", a),
    [a, b] => format!("{} and {} like this", a, b),
    [a, b, c] => format!("{}, {} and {} like this", a, b, c),
    [a, b, rest @ ..] => format!("{}, {} and {} others like this", a, b, rest.len()),
  }
}

Where rest @ .. will catch all the rest of the vector items.


Another examples:

Fixed size
let arr = [1, 2, 3];
match arr {
  [1, _, _] => "starts with one",
  [a, b, c] => "starts with something else",
};

Dynamic size
let v = vec![1, 2, 3];
match v[..] {
  [a, b] => { /* this arm will not apply because the length doesn't match */ }
  [a, b, c] => { /* this arm will apply */ }
  _ => { /* this wildcard is required, since the length is not known statically */ }
};

fn foo(words: &[&str]) {
  match words {
    [] => println!("empty slice!"),
    [one] => println!("one element: {:?}", one),
    [one, two] => println!("two elements: {:?} {:?}", one, two),
    _ => println!("I'm not sure how many elements!"),
  }
}

fn foo(words: &[&str]) {
  match words {
    ["Hello", "World", "!", ..] => println!("Hello World!"),
    ["Foo", "Bar", ..] => println!("Baz"),
    rest => println!("{:?}", rest),
  }
}

fn foo(words: &[&str]) {
  match words {
    // Ignore everything but the last element, which must be "!".
    [.., "!"] => println!("!!!"),
 
    // `start` is a slice of everything except the last element, which must be "z".
    [start @ .., "z"] => println!("starts with: {:?}", start),
 
    // `end` is a slice of everything but the first element, which must be "a".
    ["a", end @ ..] => println!("ends with: {:?}", end),
 
    rest => println!("{:?}", rest),
  }
}

refs:

Rust Box

What is the "Box" in Rust Lang

Reading some Rust code examples in documentation and blogs, I can see the use of Box<T> declaration. Like:

struct Schema {
    commands: Vec<Box<dyn Migration>>,
}

According to the official Rust documentation: "All values in Rust are stack allocated by default. Values can be boxed (allocated on the heap) by creating a Box<T>. A box is a smart pointer to a heap allocated value of type T. When a box goes out of scope, its destructor is called, the inner object is destroyed, and the memory on the heap is freed."

ref: https://doc.rust-lang.org

Random Strings in Rust

How to generate a random fixed size string in Rust

For generate a random string with fixed size in Rust, we can use Alphanumeric trait from rand crate:

use rand::{distributions::Alphanumeric, Rng}; // 0.8
 
fn main() {
  let s: String = rand::thread_rng()
    .sample_iter(&Alphanumeric)
    .take(7)
    .map(char::from)
    .collect();
  println!("{}", s);
}
 

ref: https://stackoverflow.com/questions/54275459/how-do-i-create-a-random-string-by-sampling-from-alphanumeric-characters

Rust + SQLite

Connect SQLite in a Rust app

For run SQL queries on SQLite database from Rust, we can use the sqlite3 crate:

let connection = sqlite::open(":memory:").unwrap();
 
connection
  .execute(
    "
    CREATE TABLE users (name TEXT, age INTEGER);
    INSERT INTO users (name, age) VALUES ('Alice', 42);
    INSERT INTO users (name, age) VALUES ('Bob', 69);
    ",
  )
  .unwrap();
Open a connection, create a table, and insert some rows.

connection
  .iterate("SELECT * FROM users WHERE age > 50", |pairs| {
    for &(column, value) in pairs.iter() {
      println!("{} = {}", column, value.unwrap());
    }
    true
  })
  .unwrap();
Select some rows and process them one by one as plain text:

And more...

ref: https://docs.rs/sqlite3/latest/sqlite3/