Feeling really stupid -- trying to replicate Python's `range()` function in Rust

I am very confused about this. Rust Playground

fn main() {
    let v : Vec<usize> = vec![0..20].into_iter().map(|_| 1).take(10).collect();
    assert_eq!(v.len(), 10);
    println!("{:?}", v);
}

I can't figure out why v.len() is 1, not 10, as I was expecting.

How do we replicate python range(10) in Rust?

Your initial vec vec![0..20] contains only one item of type Range. You map that to one item 1 and then collect. You still have one item.

To make a range just use 0..20 directly:

(0..20).into_iter().map(|_| 1).take(10).collect()
// ^^^^
7 Likes

Thanks, @erelde. After I posted this, I changed the inner map to |x| 1 +x and got a compilation error: x can't be added to 1 because it is a Range. I thought iter/into_iter will give me elements of the Range. Still a bit confused!

It would if called on Range, but not if called on Vec.

It’s simple: vec![0..20] is like vec![(0..20)], or like

let the_range: Range<i32> = 0..20;
vec![the_range] // : Vec<Range<i32>>

it’s comparable to

let the_string: String = String::from("Hello World");
vec![the_string] // : Vec<String>

i.e. resulting in a single-element Vec.

A multi-element Vec with multiple ranges would look like e.g. vec![0..20, 0..30, 1..5, 100..200], this one has 4 elements.

8 Likes

If that helps you, it's totally valid to have a list of ranges:

let ranges = vec![1..10, 2..20, 3..30];

and then have something using that:

for range in ranges {
     println!("{}", range.contains(&15));
}

You were not calling into_iter on the range, but on the vec.

1 Like

The point is, vec![value] is a one-element vector containing only value. In your case, the value happens to be a range, which does not undergo any special treatment: you'll still have a single-element vector with a range as its only element. If you map over that vector, the returned iterator will yield ranges and pass them to the mapped function. There's nothing magical going on, this is how it should behave.

I don't know where you get this code from, but I'm
pretty sure no official documentation explicitly (and mistakenly) tells the reader to use vec![range] to crate a vector of indexes. That'd be (0..20).collect::<Vec<_>>().

I don't know where you get this code from, but I'm

I didn't read carefully: PathBuf in std::path - Rust

I can understand the mistake; I wouldn’t ever write it myself, but reading the code, it took me several looks over the code to catch the mistake. Especially considering that .. appears with special meaning in slice patterns, too, I feel like it’s a natural thing to mis-interpret them to have special meaning when they don’t in vector or array expressions. I wouldn’t mind e.g. a helpful clippy lint that encouraged you to write vec![(0..20)] instead of vec![0..20] to make it more clear.

5 Likes

Calling into_iter on an Iterator looks a bit weird. I thought it’s useful only when you need a type parameter to be either Iterator or IntoIterator, am I wrong?

1 Like

It's unnecessary here, but I left it there for didactic purposes :thinking:

For posterity, if anybody stumbles on this thread.
Two ways to create a size 10 Vec of 1s:

let v1: Vec<usize> = [1; 10].to_vec();
let v2: Vec<usize> = (0..).map(|_| 1).take(10).collect();

Update:

let v3: Vec<usize> = repeat(1).take(10).collect();
let v4: Vec<usize> = vec![1; 10];
1 Like

A better way that doesn't allocate a huge stack array and doesn't use an unnecessary (ignored) mapping would be repeat(1).take(10).collect().

1 Like

Errr, or just

let v : Vec<usize> = vec![1; 10];

?

6 Likes

Do you mean something like this?

fn main() {
    let v: Vec<usize> = (0..100).take(10).collect();
    assert_eq!(v, [0,1,2,3,4,5,6,7,8,9]);
    println!("{:?}", v);
    
    let y: Vec<usize> = v.into_iter().map(|x| x + 1).collect();
    assert_eq!(y, [1,2,3,4,5,6,7,8,9,10]);
    println!("{:?}", y);
}