[rust-dev] std::num::pow() is inadequate / language concepts
remarcg at gmx.net
Fri Jul 25 06:26:59 PDT 2014
> Firstly, blanket statements like "This makes generic programming
> impossible" and "it does not allow proper software design" are
> unneccesary hyperbole, and do not help the discussion in any way.
You're not right, my statement wasn't blanket, it was my result
after I tried to overwork the big integer library, and I have mentioned this:
I gave up at all. (I'm doing software design and implementation since
more than 30 years, and I never accept compromises, this is the way
how to develop magnificient software).
> Traits provide a more well-defined, easier to reason about alternative
> to overloading. They do require the author of an algorithm to decide
> ahead of time whether this algorithm needs to be specializeable, which
> I guess C++-style overloading does not.
Yes, the traits are great, I'm impressed, as I said before, and in fact Rust
is really great, despite a few facts, otherwise I wouldn't subscribe to
this mailing list. And my goal is to be constructive, don't worry if I'm
a bit euphoric, such things happens. Nethertheless, it gave up to overwork
the big integer libary because I cannot specialize std::num::pow(). There is
no way to proceed with a proper design.
> Whether that is a good or a
> bad thing is debatable, but it is not true that Rust lacks a feature
> for specialization.
There is a lack in the current language concept, std::num::pow()
is inadequate due to this language concept, and std::num::pow() is
only one example for this fact.
I will repeat the problem with signatures. Currently pow() is declared
pub fn pow<T: One + Mul<T, T>>(mut base: T, mut exp: uint) -> T;
That't 100% ok. The user will call this function in this way:
pow(a) // a is i32
Perfect. Now I need a specialized function for BigInt:
pub fn pow(base: &BigInt, mut exp: uint) -> T;
There's a problem (beside the missing overloading feature): the
specialized version requires a reference. Same problem if I'm
calling this function:
pow(&a) // a is BigInt
The user has to know how to call a function, depending on the type.
But a proper function specialization would be:
pub fn pow(base: BigInt, mut exp: uint) -> T;
And so the function call is as expected, like with other numeric types:
pow(a) // a is BigInt
But there is now a problem in this function definition, BigInt is given as
a copy, and this is a software design issue (superfluous memory allocation).
And this currently happens if the user is calling std::num::pow() with a
numeric type like BigInt (apart from other performance penalties in pow()).
That's what I've mentioned that the compiler should decide whether an
argument is given by reference or by value. In this way the latter approach
works. And in the case that a function willl modify an argument (in-out
value), for example:
fn mul_vec(acc : &mut [BigDigit], base: &mut [BigDigit], mut exp:uint)
the call of this function would be:
mul_vec(&a, &b, exp)
This concept will not change, because here it has to be clear that an argument
will be changed (furthermore the compiler should give a warning if a function
is not changing a mutable argument). I think that this approach is even
superior to the 'const' concept of C++, and it fit's with the great overall
concept of Rust (especially with the owner/borrower concept).
I try to show the problems if function specialization (overloading) is not
supported. A stable software design is problematic. Adding a new module,
which will use existing function declarations, is impossible in some cases.
Currently I cannot implement a specialized version of pow() for BigInt, adding
a new function for a different numeric type is only a hack, and moving this
function into a trait is not solving the general problem, because pow() is
only one example. (Beside: it's not my decision to move pow() into a trait.)
-------------- next part --------------
An HTML attachment was scrubbed...
More information about the Rust-dev