24 days of Rust - from_fn

Important note: this article is outdated! Go to http://zsiciarz.github.io/24daysofrust/ for a recent version of all of 24 days of Rust articles. The blogpost here is kept as it is for historical reasons.

In Rust there is no concept of a constructor as a language feature, like for example in C++. However there is a strong convention (and mentioned in the guidelines) to use a static method called new as the constructor. This works well, but you can have only one function called new in the impl - there is no method overloading. So are we out of luck if we want to have different constructors? No! But arguably different purposes should imply different method names, so another convention is to prefix extra constructors with with_ (such as Vec::with_capacity) or from_ , if the constructor does some kind of a conversion.

A few types in the standard library and some third-party crates provide a from_fn constructor. This curious method usually takes as arguments some sort of dimensions and a closure that will generate values.

Vectors

One of the typical methods to create a vector is to call collect() on an iterator like below:

let v = range ( 0 , 10 u ). map ( | x | x * 3 ). collect ::< Vec < _ >> (); println ! ( "{}" , v );

We can shorten that a bit with the from_fn method. It takes the expected vector size and a closure that uses current index to generate vector elements.

let v = Vec :: from_fn ( 10 , | x | x * 3 );

This is roughly analogous to the following C++ code:

vector < int > v ; int x = 0 ; generate_n ( back_inserter ( v ), 10 , [ & i ] () { return ( x ++ ) * 3 ; });

We can of course ignore closure's argument and do something else, like generate a vector of random elements:

use std :: rand ; let v = Vec :: from_fn ( 10 , | _ | rand :: random ::< uint > ());

Matrices

In the nalgebra crate there is a DMat type representing a matrix which dimensions are known at runtime. We can build a matrix using the from_fn constructor too. Let's create a triangular matrix:

extern crate nalgebra ; use nalgebra :: DMat ; let mat : DMat < uint > = DMat :: from_fn ( 7 , 7 , | i , j | if j <= i { 1 } else { 0 }); println ! ( "{}" , mat );

The first two arguments to from_fn are numbers of rows and columns; this means the closure must take two arguments - indices of the current row and column. And here's our matrix:

$ cargo run 1 0 0 0 0 0 0 1 1 0 0 0 0 0 1 1 1 0 0 0 0 1 1 1 1 0 0 0 1 1 1 1 1 0 0 1 1 1 1 1 1 0 1 1 1 1 1 1 1

Images

Bur we're not limited to mathematical objects. For example the image crate provides a buffer that can generate the image with the from_fn call.

extern crate image ; use image :: Pixel ; let buffer = image :: ImageBuffer :: from_fn ( 512 , 512 , | x , y | { Pixel :: from_channels (( x * y % 256 ) as u8 , ( y % 256 ) as u8 , ( x % 256 ) as u8 , 255 ) }); let img = image :: DynamicImage :: ImageRgba8 ( buffer ); let out = File :: create ( & Path :: new ( "out_pattern.png" )). unwrap (); let _ = img . save ( out , image :: PNG );

We're working in two dimensions in the same manner as with the DMat type. And here's the generated image:

Code examples in this article were built with rustc 0.13.0-nightly.

Photo by Tony Hisgett and shared under the Creative Commons Attribution2.0 Generic License. See https://www.flickr.com/photos/hisgett/3031904374

Napisane 17 grudnia 2014