avatarapplied.math.coding

Free AI web copilot to create summaries, insights and extended knowledge, download it at here

3145

Abstract

ted to contemplate how you would solve this in your favorite (statically typed) language.</p><h2 id="2e29">Solution:</h2><p id="5c42">To solve our problem we will utilize Rust’s <code>Trait</code> system. The above situation is a very good example to remember for what reason traits do exist in Rust’s ecosystem.</p><p id="e7af">We define the following trait:</p><div id="d3b8"><pre><span class="hljs-keyword">pub</span> <span class="hljs-keyword">trait</span> <span class="hljs-title class_">Sin</span><T> { <span class="hljs-keyword">fn</span> <span class="hljs-title function_">sin</span>(v: &<span class="hljs-type">Vec</span><T>) <span class="hljs-punctuation">-></span> <span class="hljs-type">Vec</span><T>; }</pre></div><p id="95f5">This just replicates our method signature but using a generic type parameter <code>T</code>. But it provides much to our code: “Sine is now a trait”. Let us compare this to Rust’s internal trait <code>Add</code>. If a type implements <code>Add</code>, then we are able to use the operator <code>+</code> to connect instances of it: <code>a + b</code>. For sine though there is no internal way of stating something like “the type is Sine-able”. With the above trait we have added exactly this.</p><p id="9e88">Now, we can implement the two versions of our utility function plus the matrix version:</p> <figure id="fab8"> <div> <div>

            <iframe class="gist-iframe" src="/gist/rust-play/f6d78613930c78ee7d4c348a7bc7dd01.js" allowfullscreen="" frameborder="0" height="undefined" width="undefined">
          </div>
        </div>
    </figure></iframe></div></div></figure><p id="36c3">Okay, runs as well!</p><p id="1997">One thing deems interesting to point out here. If a consumer includes our <code>Utils</code> as a module (say <code>mod utils</code>), none of both versions will be available on <code>Utils</code> without not doing a <code>use utils::Sin</code>. That is, the trait must be brought into context before being able to use it. This sounds cumbersome but adds very much security to our code! Assume, there is another declaration of <code>Sin</code> from another module. This way, you can be certain which one is being used in your code.</p><p id="9cfb">A second very neat thing Rust’s compiler is doing, it only adds those implementations to the final machine code that are actually used. So, if our consumer only uses <code>Sin</code> on <code>Vec&lt;f64&gt;</code>, only the corresponding <code>Sin&lt;f64&gt;</code> implementation is compiled.</p><p id="ddb2">Looking at the above code, beside all this advertisement of Rust, some might (hopefully) started complaining about the code replication. This <code>impl</code> for <code>Sin&lt;f32&gt;</code> and <code>Sin&lt;f64&gt;</code> structurally look the same. Of course, from a compilers way of looking at this and even from the runtime, these things are totally different (bit-level). Let us see next how Rust helps out here as well.</p><h2 id="1d35">Avoiding code replication:</h2><p id="f7c3">From a security point of view to writ

Options

e both versions for <code>Sin<f32></code> and <code>Sin<f64></code> looks correctly. From a developers perspective it looks bad since it introduces harder to maintain code. So, we could sum-up: On the one hand we want to have both implementations in the code but only want to write it once. Here comes Rust’s <code>Macros</code> into play that allows to make the compiler writing code for us. Let us see how this works:</p> <figure id="459e"> <div> <div>

            <iframe class="gist-iframe" src="/gist/rust-play/47f07f8b0f07f54075eeefaa0a4cab75.js" allowfullscreen="" frameborder="0" height="undefined" width="undefined">
          </div>
        </div>
    </figure></iframe></div></div></figure><p id="27c7">All the <code>impl</code> have been replaced by the macro definition <code>sin_impl_macro</code> and further four application of it <code>sin_impl_macro!…</code>. The macro itself consists of two parts:</p><p id="a145">The one that provides implementations for <code>Sin&lt;T&gt;</code> and <code>T</code> being either <code>f32</code> or <code>f64</code>,</p>
    <figure id="680a">
        <div>
          <div>
            
            <iframe class="gist-iframe" src="/gist/rust-play/a17f93df307012b142b77e3e79e27e42.js" allowfullscreen="" frameborder="0" height="undefined" width="undefined">
          </div>
        </div>
    </figure></iframe></div></div></figure><p id="7abe">And the other providing implementations for <code>Sin&lt;Vec&lt;T&gt;&gt;</code>,</p>
    <figure id="2362">
        <div>
          <div>
            
            <iframe class="gist-iframe" src="/gist/rust-play/720e84ba7f248728f65a298ea83b10ab.js" allowfullscreen="" frameborder="0" height="undefined" width="undefined">
          </div>
        </div>
    </figure></iframe></div></div></figure><p id="10d5">As you can see, the code inside does only abstracts the use of the type variable <code>T</code>. The rest of both implementations are exactly the once as before.</p><p id="c04e">Also, interesting is, we have here another sort of ‘overloading’. The macro when applied is matching the patterns supplied in the arms. That is,</p><div id="4b2f"><pre>sin_impl_macro!(f32);</pre></div><div id="725a"><pre>sin_impl_macro!(f64);</pre></div><p id="2670">are matched with <code>($T:tt) =&gt; …</code></p><p id="f571">and</p><div id="7ef1"><pre>sin_impl_macro!(<span class="hljs-type">Vec</span>&lt;<span class="hljs-type">f32</span>&gt;);</pre></div><div id="ea62"><pre>sin_impl_macro!(<span class="hljs-type">Vec</span>&lt;<span class="hljs-type">f64</span>&gt;);</pre></div><p id="9b2c">with <code>(Vec&lt;$T:tt&gt;) =&gt; …</code></p><p id="9b93">I think we can be satisfied with the final product. Of course, macros are a nice help, but one should not overdo things with it. It can quickly become hard to understand. But let me mention, debugging works as before, since at the end, a macro is just writing code at compile time. Nothing else!</p><p id="0d98">Thanks for reading!</p></article></body>

How to overload methods in Rust.

Overloading methods is a commonly used feature in many programming languages. Before turning to further details, let me mention early, in Rust there exists no exact ‘overloading’ of functions. But nevertheless, Rust makes available some techniques that are of upmost importance to avoid code replication and at the same time ensure security as well as its glorified performance.

Note, this story assumes the reader to have some basic knowledge of Rust. If necessary you might find this a helpful introduction.

The problem:

Let us consider the following simple scenario. We want to create a small libraries Utils that operates on vectors (or matrices) and allows to compute the sine of a vector be component-wise application. That is, we want to write code of the form

let w = Utils::sin(&v);

Here v could be something like vec![1.0, 2.0] and w would be expected to become vec![(1.0).sin(), (2.0).sin()].

First approach:

This worked! But wait, this is written to only support vectors of f32‘s. We also want to support f64‘s or even vectors of vectors (matrices) like Vec<Vec<f32>>. By just adding a corresponding version to the impl of Utils results in the error

duplicate definitions with name `sin`:
duplicate definitionrustcE0201

Let us next see what kind of mechanism Rust provides for our scenario. You are also invited to contemplate how you would solve this in your favorite (statically typed) language.

Solution:

To solve our problem we will utilize Rust’s Trait system. The above situation is a very good example to remember for what reason traits do exist in Rust’s ecosystem.

We define the following trait:

pub trait Sin<T> {
   fn sin(v: &Vec<T>) -> Vec<T>;
}

This just replicates our method signature but using a generic type parameter T. But it provides much to our code: “Sine is now a trait”. Let us compare this to Rust’s internal trait Add. If a type implements Add, then we are able to use the operator + to connect instances of it: a + b. For sine though there is no internal way of stating something like “the type is Sine-able”. With the above trait we have added exactly this.

Now, we can implement the two versions of our utility function plus the matrix version:

Okay, runs as well!

One thing deems interesting to point out here. If a consumer includes our Utils as a module (say mod utils), none of both versions will be available on Utils without not doing a use utils::Sin. That is, the trait must be brought into context before being able to use it. This sounds cumbersome but adds very much security to our code! Assume, there is another declaration of Sin from another module. This way, you can be certain which one is being used in your code.

A second very neat thing Rust’s compiler is doing, it only adds those implementations to the final machine code that are actually used. So, if our consumer only uses Sin on Vec<f64>, only the corresponding Sin<f64> implementation is compiled.

Looking at the above code, beside all this advertisement of Rust, some might (hopefully) started complaining about the code replication. This impl for Sin<f32> and Sin<f64> structurally look the same. Of course, from a compilers way of looking at this and even from the runtime, these things are totally different (bit-level). Let us see next how Rust helps out here as well.

Avoiding code replication:

From a security point of view to write both versions for Sin<f32> and Sin<f64> looks correctly. From a developers perspective it looks bad since it introduces harder to maintain code. So, we could sum-up: On the one hand we want to have both implementations in the code but only want to write it once. Here comes Rust’s Macros into play that allows to make the compiler writing code for us. Let us see how this works:

All the impl have been replaced by the macro definition sin_impl_macro and further four application of it sin_impl_macro!…. The macro itself consists of two parts:

The one that provides implementations for Sin<T> and T being either f32 or f64,

And the other providing implementations for Sin<Vec<T>>,

As you can see, the code inside does only abstracts the use of the type variable T. The rest of both implementations are exactly the once as before.

Also, interesting is, we have here another sort of ‘overloading’. The macro when applied is matching the patterns supplied in the arms. That is,

sin_impl_macro!(f32);
sin_impl_macro!(f64);

are matched with ($T:tt) => …

and

sin_impl_macro!(Vec<f32>);
sin_impl_macro!(Vec<f64>);

with (Vec<$T:tt>) => …

I think we can be satisfied with the final product. Of course, macros are a nice help, but one should not overdo things with it. It can quickly become hard to understand. But let me mention, debugging works as before, since at the end, a macro is just writing code at compile time. Nothing else!

Thanks for reading!

Rust
Overloading
Macros
Traits
Scientific Computing
Recommended from ReadMedium