4 ms·
I see your point, but I've got some practical objections to it: a) You can't (sanely) initialize a fixed-sized rust array with an iterator. It looks like one
by xscott 5y ago
I see your point, but I've got some practical objections to it:
a) You can't (sanely) initialize a fixed-sized rust array with an iterator. It looks like one of the following:
let a = [1.0f64, 2.0, 3.0]; // not convenient for large arrays
let a = [1.0f64; 1000]; // large array, but only the same value
I did find an insane example using an unsafe block. I've got no moral qualms about unsafe blocks, but using them to initialize an array seems heavy.
b) If you want it dynamically sized, you're probably going to use `Vec` and maybe an iterator as you suggested. I didn't try to compile the following, but I don't think it looks better than the for loop:
let len = get_command_line_argument() as isize;
let a: Vec<f64> = (0..len).map(|ii| sinc(scale*(ii - len/2) as f64)).collect();
You could break it down with more .maps, but I think it'd be even worse to look at. I think it's also growing iteratively (amortized constant, but still reallocating and copying along the way).
c) There are obvious cases where you want to re-use rather than reallocate your arrays. In addition to the cost of allocation and initializing to the wrong value, there are other times you might like to re-use the existing array address (FFI, etc..). You'd probably want to keep it in a Box<[T]> instead of a Vec<T>, but the point stands.
For (b), I suppose you could write a helper function:
let a = make_vec(len, |ii| sinc(scale*(ii - len/2) as f64));
That's not awful. Both len and ii can be isize, and the implementation of make_vec can reserve the size in advance. Maybe I'll add something like that to my toolbox :-)
- tialaramex 5y ago> I think it's also growing iteratively (amortized constant, but still reallocating and copying along the way). Nah. Let's see why that isn't the case because it's instructive: 0..len is a Range, Range implements core::iter::TrustedLen which is an unsafe trait that says "I promise" (hence it must be unsafe) "that my size hint isn't a hint at all but my actual exact size". [ Rust's "built-in" containers and iterator adaptors eagerly implement TrustedLen, you should consider doing likewise if you make things that you're absolutely certain know their correct size and wouldn't otherwise be TrustedLen ] Because Range implements TrustedLen the Map also chooses to implement TrustedLen since it doesn't change the length and can just pass along what Range said. The iterator.collect() call effectively ends up as spec_extend(iterator) on the Vec, and spec_extend() for an iterator with TrustedLen uses the size hint to reserve() the appropriate amount of space in the vector up front.
- xscott 5y agoFeels like archaeology or a treasure hunt to track that down, but yeah it all looks very clever: https://doc.rust-lang.org/src/core/iter/traits/iterator.rs.html#1774-1776 https://doc.rust-lang.org/src/core/iter/traits/iterator.rs.h... (collect) https://doc.rust-lang.org/src/alloc/vec/mod.rs.html#2549-2554 https://doc.rust-lang.org/src/alloc/vec/mod.rs.html#2549-255... (FromIterator for Vec) https://doc.rust-lang.org/src/alloc/vec/spec_from_iter.rs.html#37 https://doc.rust-lang.org/src/alloc/vec/spec_from_iter.rs.ht... (SpecFromIter for Vec) https://doc.rust-lang.org/src/alloc/vec/spec_extend.rs.html#22 https://doc.rust-lang.org/src/alloc/vec/spec_extend.rs.html#... (SpecExtend for Vec) https://doc.rust-lang.org/src/core/iter/range.rs.html#854 https://doc.rust-lang.org/src/core/iter/range.rs.html#854 (TrustedLen for Range) https://doc.rust-lang.org/src/core/iter/range.rs.html#17 https://doc.rust-lang.org/src/core/iter/range.rs.html#17 (TrustedStep for isize) The `TrustedLen for Range` is marked unstable, and the issue tracker has comments from just 8 days ago. Does that mean this only works with nightly? Back to the topic though,the iterator/collect solution looks uglier than the for loop to my eyes, and I still can't think of any (reasonable) case where unsigned array/slice/vec indexes are better. :-)
- LegionMammal978 5y agoThe compiler and standard library are allowed to use whatever unstable features they want. So really, things like TrustedLen can be seen as implementation details that nightly programs can tap into, provided they're not worried about building on all future versions of the compiler.
- xscott 5y agoInteresting. Just to be clear though: if I'm up to date using rustup with stable, `Vec` and `collect` _are_ using this?
- tialaramex 5y agoThey've been using this for a long time. I actually have a checkout of Rust's git from summer 2021 when I was contemplating doing a bunch of doc changes (to use arrays instead of Vec in examples where that was now reasonable and maybe clearer) sitting around and used that to quickly verify what you summarised above when I answered. So this was all true in like 1.53 or whatever that checkout is, and presumably long before. There are quite a few of these unstable Traits laying about in Rust's standard library that express important ideas that we can't live without, and yet are not something that's nailed down solidly enough to stabilize, the standard library is allowed to do this while claiming to be stable even though we are not - the rationale is that if they rip out one of these unstable traits, they can do the work to fix all the mess and deliver stability in the stable API anyway, whereas the same could not be true for some third party crate. Another unstable but useful feature, check out Pattern: https://doc.rust-lang.org/std/str/pattern/trait.Pattern.html https://doc.rust-lang.org/std/str/pattern/trait.Pattern.html Rust's string matching code uses Pattern everywhere so that even though Rust does not have ad hoc polymorphism (method overloading in C++): "clowns".contains("owns") // That's a literal string yet also "clowns".contains('c') // That's not a string it's just one character and "clowns".contains(char::is_lowercase) // A method on characters which here functions as a simple predicate A sound API and yet also more convenient and more extensible.