rust-lang / rust-lang/rfcs

Consider making operators transparently work on borrows wherever possible

Open
#1,936 2 comments 16 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

T-lang
Dominant language
Markdown
Stars
6.6k
Forks
1.7k
Avg merge
16h 14m
Merged PRs (30d)
1

Description

Hello!

I've been working on a pure-Rust elliptic curve cryptography library with @hdevalence, called curve25519-dalek. We've implemented operators Add, Sub, Mul, Neg, for the field and, similarly, Add and Sub for elliptic curve points. At first, we implemented the operators on the types T. (T in this case is some representation of a point, comprised internally four FieldElements). But that was obviously sloooooow, because now everytime you want to add two points together, you're copying all those internal FieldElements around (and each FieldElement is itself, internally, basically an [i32; 10]). With that implementation, we were copying 160 bytes around everytime we did any point operation. We then switched to implementing them for &T to avoid copies, e.g.:

impl<'a,'b> Add<&'b PreComputedPoint> for &'a ExtendedPoint {
    type Output = CompletedPoint;

    fn add(self, other: &'b PreComputedPoint) -> CompletedPoint {
        let Y_plus_X  = &self.Y + &self.X;
        let Y_minus_X = &self.Y - &self.X;
        let PP        = &Y_plus_X  * &other.y_plus_x;
        let MM        = &Y_minus_X * &other.y_minus_x;
        let Txy2d     = &self.T * &other.xy2d;
        let Z2        = &self.Z + &self.Z;

        CompletedPoint{
            X: &PP - &MM,
            Y: &PP + &MM,
            Z: &Z2 + &Txy2d,
            T: &Z2 - &Txy2d
        }
    }
}

Voilá, fast as hell! No more copies. And it's still quite readable. However, now, as I'm implementing the Elligator2 mapping (a way encode a point into a uniformly random string), I'm started to see the ampersands pile up pretty quickly:

pub fn elligator2(X: &FieldElement) -> UniformRepresentative {
    let one:    FieldElement = FieldElement::one();
    let n:      FieldElement = FieldElement([2, 0, 0, 0, 0, 0, 0, 0, 0, 0]); // n = 2
    let nrr:    FieldElement = &n * &X.square();                             // nr²
    let mut u:  FieldElement = &(-(&A)) * &(&one + &nrr).invert();           // u = -A/(1 + nr²)
    let w:      FieldElement = &u * &(&(&u.square() + &(&A * &u)) + &one);   // w = u(u² + Au + 1)
    let uprime: FieldElement = &(-(&A)) - &u;
    // If u and u' are integers modulo p such that u' = -A - u and u/u' = nr²
    // for any r and fixed nonsquare n, then the Montgomery curve equation
    // v = u(u² + Au + 1) has a solution for u = u or u = u', or both.
    //
    // From the above lemma, it follows that u = -A/(1 + nr²) and
    // u' = -Anr²/(1 + nr²). Thus, given r, we can easily calculate u and u' and
    // use the Legendre symbol to choose whichever value gives a square w.
    let nonsquare: u8 = legendre_is_nonsquare(&w);

    // If w is non-square, then we recompute u to be u' = -A - u:
    u.conditional_assign(&uprime, nonsquare);

    UniformRepresentative(u)
}

let mut u: FieldElement = &(-(&A)) * &(&one + &nrr).invert();

Wat. I just wanted u = -A/(1 + nr²)!

let w: FieldElement = &u * &(&(&u.square() + &(&A * &u)) + &one);

Wat the wat. I wanted w = u(u² + Au + 1).

It seems @aturon and others have discussed potential solutions to this issue on Reddit, namely providing some "auto-ref" functionality, similar to &self.

Is this a change Rust would like to see? Are there more concerns which might arise if this were implemented? I really want to have the "my code is pretty" cake and eat the "my code is fast" cake too. :)

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the operator implementations described in curve25519-dalek's field.rs and curve.rs, then read the linked Reddit discussion about by-value operator overloading. Define the desired auto-reference behavior and its language-level concerns before proposing an RFC; done means the semantics and tradeoffs are documented clearly enough for Rust maintainers to evaluate.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
compilers
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.