I was pretty tired last night when I wrote that previous reply to you, and I don't think I stated my complaints very clearly. In several of the places where I quote you below, it's not your fault you didn't understand what I was getting at. Generally, I was trying to be concise to make my point, but it still turned into a wall of text, and I don't think I succeeded at either goal (making my point or being concise). Sorry in advance, but this message is a wall of text too.

> Yeah, I thought you were implementing a vector or a doubly linked list or something, not a matrix.

I was trying several things. For example, I got an immutable finger tree working (comparable difficulty to linked lists), but I was trying to describe the pain points, not where I succeeded. Please remember, these were all learning exercises for me, so I wasn't concerned about whether or not there was an existing library that did what I was trying to do - I wanted to learn how to write that kind of library.

Early on, I did intend to re-implement Vec. That is non-trivial in Rust and C++ for reasons I think we might both agree on, although honestly I did wish it was easy in Rust. I didn't think that should involve unsafe code, but it really seems like it does, so life goes on.

Later, I wanted to build a Matrix type wrapping around a low level array of memory. I did not want to wrap a Matrix around a Vec because I don't need a growable storage area, and I don't think I should have to pay for a capacity and current length field when I already keep track of the number of rows and columns. Reading the docs, it seemed like what I wanted was a "slice" to act as my array. I figured Vec must use a slice internally, so I should be able to do that as well! Diving through the Vec source code, I see RawVec, Unique, NonZero< * T> and finally a pointer. There's also something in there about PhantomData which I suspect is related to the phantom type stuff I've read about in Ocaml, but I figured I didn't really need to understand that right away. Translating to pseudo C++, this looks something like:

struct Vec<T> {
struct RawVec<T> {
struct Unique<T> {
struct NonZero<const T*> {
tuple<T*> value;
} pointer;
struct PhantomData<T> {
} _marker;
} ptr;
usize cap;
} buf;
usize len;
}

Piecing this together, my first thought was "crap, why is something as simple as Vec so complicated?". My second thought was "uh oh, where's the slice?". My next thought was, "well hmm, I can copy all that stuff", followed up by, "but a lot of those are marked as unstable, so I probably shouldn't do that".

So I poked around all over the docs and asked some questions about how to get a slice, and it seemed like I would need a Box to hold it. Even today, I really don't know if that's what I wanted, but mostly I gathered that you can't create one from a runtime chosen size without some unsafe code somewhere. That's when I found Vec::into_boxed_slice() which seemed to do almost what I wanted. I didn't want a Vec, and the implementation of that method has an unsafe block, so I used it and moved on. That's the first code snippet I included in the previous message.

In a previous post, you said you were interested in finding out what causes new Rust users to stumble. For me, I got hung up trying to figure out how to create a fixed sized array. Please don't say, "just use a Vec!" - that really misses the point. I'm still not sure what type I should use, but if it is a Box<[T]>, I would love a function like:

slice::from_func<T>(len: usize, f: &Fn(usize) -> T) -> Box<[T]>

where f is called once for each element to provide a value. If something else is the right choice, imagine a different return type.

> That code isn't unsafe. It uses the [T] DST, which isn't a common type, but it's safe code.

Maybe DSTs are what I was looking for, but I see the docs are in the "nomicon", and I'm not sure that even existed when I was trying to figure out the stuff above. I will read it in the near future though.

> [the num crate] does give you the Float and Signed traits, which get you exactly what you need here? You may need to cast to float for integers. It's not great, but it should be good enough.

I didn't really want to dive into the ways I think the num crate is broken, but here goes. Floating point numbers and signed integers are not the only thing I want to call .abs() on. For instance, .abs() is relevant to complex numbers too (as are exp() and many others). Unfortunately, you can't implement Float or Signed for complex numbers because those traits have many other methods which don't make any sense for complex numbers. As example, Signed requires .is_positive() and Float requires .min_value().

Let's dodge the topic about calling .abs() on unsigned types, but in generic code that's really not as silly as it sounds. The num crate is trying to solve an ugly problem, and I don't think it's made the right trade offs. Really it isn't good enough, and I'm glad you guys removed it from the std library before version 1.0 and committing to it for the foreseeable future.

> I mean, operators have the same conflicting impl problem regardless of how you implement them (as traits or otherwise).

I don't see the conflict. These impls are fine:

impl Mul<f64> for Matrix<f64>
impl Mul<Matrix<f64>> for f64

And I can write a macro to call on any type I want. Try to use generics instead of a macro though, and the second one breaks.

> C++ solves this with overload resolution rules. Rust doesn't want that, and instead doesn't allow overlaps to occur in the first place.

I understand why Rust doesn't want SFINAE, and I understand why coherence is valuable, but you shouldn't believe something like SFINAE is required to make generic operators work. I've read nikomatasaki's blog posts several times, and it seems like he almost went with the "covered" rule. If the "covered" rule was applied to binary operator traits, but the current (nuanced /cough) rules applied everywhere else, I think the kind of generic operators I want to write would pass coherence. I'm also not saying this is the only way it could work, but I am saying that generic binary operators really should work symmetrically.

> I think C++ has a similar asymmetry when it comes to choosing between overlapping operator overloads when the overlap is in the pre or in the post type

That might be true if you define the operator as a member, so don't do that. If you declare it as a standalone function, these are really very general and symmetric:

template<class A, class B>
auto operator *(const A& a, const Matrix<B>& b)
-> Matrix<decltype(A() * B())>
{ /* implementation */ }

template<class A, class B>
auto operator *(const Matrix<A>& a, const B& b)
-> Matrix<decltype(A() * B())>
{ /* implementation */ }

> But here's the thing -- you're not actually doing anything with these.

Please don't assume that because I provided short examples of things which don't work like I think they should that you have any idea what I do. I'm not some novice who gets a kick out of finding ways to break the language. I understand why you might think that, but you're wrong, and it's condescending. I was trying to create the tools I would need for the kind of work I do, and I stumbled in several places. I'd like to ignore this part of your reply and get back to the rest of it.

> You're designing a library for general use, but you're designing it with the concerns for use in C++.

No. I was trying to design it with my understanding of idiomatic Rust, and I was trying to learn the language. We're only discussing C++ because it's the topic of this thread and because Rust aspires to be an alternative to it.

> Asymmetric operators are considered bad in C++. Not in Rust; many operators are asymmetric.

You state that like it was a well reasoned design decision instead of an oversight or unfortunate consequence, and I don't believe that is true. If you read nikomatsakis's blog post from my point of view, it looks like it was basically an accidental casualty because it wasn't in his list of use cases. Why wouldn't you want them to be symmetric? The num crate uses macros to implement symmetric operations on complex numbers in exactly the way I've described above. If asymmetry is something valuable, why would they do that?

> But ultimately, how much do asymmetric operators really affect you when programming? Just reorder the things. No big deal.

Sure, I could get by in Forth or Scheme too if they met my other requirements. However, processing arrays of numeric data is something I do nearly every single day. I translate equations I know and new ones I learn from published papers all the time. The order matters for clarity, and not all operations are commutative. Why have operators at all if you aren't trying represent math notation?

Look, it's fine if you don't want Rust to appeal to numerical programmers like me - you don't owe me anything. You seemed interested in what I found lacking, so I tried to share. Honestly, I had already assumed these kinds of things won't be fixed, I've already suffered the learning process for how to work around them, and maybe they'll be revisited in Rust 2.0.

> I can make similar complaints about how hard it is to use algebraic datatypes in C++.

Yes, implementing ADTs in C++ is terrible. This is an area where Rust shines, and I greatly prefer sum types to classes and inheritance.