Just An Application

June 21, 2013

Programming With Rust — Part Fourteen: Crates, Modules And Files

Whilest adding the RequestBuffer definitions directly to the existing httpd.rc file was OK for the purposes of experimentation it is not a good habit to get into.

If we keep lobbing everything into httpd.rc before very long it will have turned into an impenetrable “wall of code”.

We need to place sets of related definitions in separate files and then specify that they are part of the crate.

1.0 Crate Files And Modules

A crate comprises an anonymous module and the items it contains.

These items are defined in the crate file.

The items in the crate file can include module items.

A module item defines a module nested within the anonymous top-level module of the crate.

A module item can define a module and the items it contains explicitly or it can reference a module contained in another file.

A module item which references a module in another file is of the form

    "mod" module_name ';'

By default this identifies the module contained in a file of the same name with the suffix “.rs”.

For example, the module item

    mod buffer;

identifies the module buffer in the file buffer.rs

2.0 Source Files And Modules

A Rust source file implicitly defines a module which contains all the items within that file.

When specified as part of a crate the name of the implicitly defined module is determined by the module item in the crate file which references it.

3.0 Moving Items Into Separate Files

We effectively have two sets of items which we can move into separate files.

  • the items which define the RequestBuffer and implement it, and

  • the function definitions and static items for listening on a socket and accepting a connection

We will move these into the files

    buffer.rs

and

    server.rs

respectively.

3.1 buffer.rs

We need to make the RequestBuffer struct and the static method new and the method readLine public so they can be accessed from outside the module. [1].

3.2 server.rs

In server.rs we need to add

    use buffer::RequestBuffer;

and we are going to rename the main function to run and add parameters for address and port so they can be passed in rather than being hard-wired

httpd.rc

In httpd.rc nearly everything has gone.

We need to add

    ...
    mod buffer;
    mod server;
    ...

We are going to hang on the IPV4_LOOPBACK and PORT static items so we can pass them to the function server::run which we are going to invoke from our new main function.

4.0 The New httpd Crate Module Structure

crate_file

5.0 The Source Code For httpd v0.4

5.1 buffer.rs

// buffer.rs

// part of httpd v0.4


use std::net::tcp::TcpSocketBuf;

// RequestBuffer

pub struct RequestBuffer
{
    priv socketBuf: TcpSocketBuf,
    priv bytes:     ~[u8],
}

//

static SIZE: uint = 4096;

// 

static CR: u8 = 13;

static LF: u8 = 10;

impl RequestBuffer
{    
    pub fn new(socketBuf: TcpSocketBuf) -> RequestBuffer
    {
        RequestBuffer { socketBuf: socketBuf, bytes: ~[0u8, ..SIZE] }
    }

    pub fn readLine(&mut self) -> ~str
    {
        self.bytes.clear();
        
        let mut state = 0;
		
        loop
        {
            let i = self.socketBuf.read_byte();
			
            if (i < 0)
            {
                fail!(~"EOF");
            }
            
            let b = i as u8;
            
            match state
            {
                0 => 
                {
                    if (b == CR)
                    {
                        state = 1;
                    }
                },
						
                1 => 
                {
                    if (b == LF)
                    {
                        return str::from_bytes(vec::const_slice(self.bytes, 0, self.bytes.len() - 1));
                    }
                    else
                    {
                        state = 0;
                    }
                },
						
                _ => 
                {
                    fail!(fmt!("state == %u !", state));
                }
            }
            self.bytes.push(b);
        }
    }
}

5.2 server.rs


// server.rs

// part of httpd v0.4

extern mod std;

use core::comm::SharedChan;

use core::option::Option;


use core::task;

use std::net::ip;
use std::net::tcp;
use std::net::tcp::TcpErrData;
use std::net::tcp::TcpNewConnection;
use std::net::tcp::TcpSocket;

use std::net::tcp::socket_buf;

use std::sync::Mutex;

use std::uv_iotask;

use buffer::RequestBuffer;



fn on_establish_callback(chan: SharedChan<Option>)
{
    io::println(fmt!("on_establish_callback(%?)", chan));
}

fn new_connection_callback(newConn :TcpNewConnection, chan: SharedChan<Option>)
{
    io::println(fmt!("new_connection_callback(%?, %?)", newConn, chan));
	
    let mx = Mutex();
	
    do mx.lock_cond
        |cv|
        {
            let mxc = ~mx.clone();

            do task::spawn 
                {
                    match tcp::accept(newConn)
                    {
                        Ok(socket) => 
                        {
                            io::println("accept succeeded");
                            do mxc.lock_cond
                                |cv|
                                {
                                    cv.signal();
                                }
                            handleConnection(socket);
				                
                        },
                        Err(error) => 
                        {
                            io::println(fmt!("accept failed: %?", error));
                            do mxc.lock_cond
                                |cv|
                                {
                                    cv.signal();
                                }
                        }
                    }
                }
            cv.wait();
        }
}

fn handleConnection(socket: TcpSocket)
{	
    let mut buffer      = RequestBuffer::new(socket_buf(socket));
    let     requestLine = buffer.readLine();
	
    io::println(requestLine);
    loop
    {
        let line = buffer.readLine();
		
        io::println(line);
        if (str::len(line) == 0)
        {
            break;
        }
    }
    io::stdout().flush();
    fail!(~"Now what ?");
}

static BACKLOG: uint = 5;


pub fn run(address: &str, port: uint)
{	
    tcp::listen(
        ip::v4::parse_addr(address),
        port,
        BACKLOG,
        &uv_iotask::spawn_iotask(task::task()),
        on_establish_callback,
        new_connection_callback);
}

5.3 httpd.rc

// httpd.rc

// v0.4

extern mod std;

mod buffer;
mod server;

static PORT: uint = 3534;

static IPV4_LOOPBACK: &'static str = "127.0.0.1";

fn main()
{	
    server::run(IPV4_LOOPBACK, PORT);
}

Notes

  • I discovered quite inadvertently that it is not actually necessary to make the RequestBuffer type public explicitly.
    The implementation seems to do this implicitly. This behaviour does not seem to be documented.


Copyright (c) 2013 By Simon Lewis. All Rights Reserved.

Unauthorized use and/or duplication of this material without express and written permission from this blog’s author and owner Simon Lewis is strictly prohibited.

Excerpts and links may be used, provided that full and clear credit is given to Simon Lewis and justanapplication.wordpress.com with appropriate and specific direction to the original content.

June 14, 2013

Programming With Rust — Part Nine: Let’s Build A Program

1.0 Defining A Main Function

We need somewhere to call the listen function from.

For now we will call it directly from the main function of our Rust program,

There are a couple of ways to define one of these.

One way is to define a function called main which takes no arguments and does not return a value, like so


    fn main()
    {	
        tcp::listen(
            ip::v4::parse_addr("127.0.0.1"),
            5,
            3534,
            &uv_iotask::spawn_iotask(task::task()),
            |chan|
            {
                io::println(fmt!("on_establish_callback(%?)", chan));
            },
            |newConn, chan|
            {
                io::println(fmt!("new_connection_callback(%?, %?)", newConn, chan));
                fail!(~"Now what ?");
            });
    }

2.0 Building A Rust Executable

In Rust terminology an executable is a crate.

The Rust compiler takes a single crate in source form and from it produces a single crate in binary form.

The source which defines a crate is contained in a single file, the crate file.

A crate in binary form can be either an executable or a library.

2.1 Linking Against Other Crates

A crate file specifies which other crates containing libraries, if any, the crate being defined should be linked against.

Every crate is automatically linked against the crate containing the core library.

In our case we also need to link against the crate containing the std library.

We specify this dependency like this

    extern mod std;

This is an example of an extern_mod_decl which is a kind of view_item.

2.2 Name Bindings

To make use of the definitions in the externally linked crates we set up some name bindings like so

    ...

    use core::task;

    use std::net::ip;
    use std::net::tcp;

    use std::uv_iotask;
    
    ...

2.3 Compiling The Program

To compile our crate file we simply do this

    rustc httpd.rc

3.0 Running The Program

The result of the compilation is an executable called httpd.

If we just run it we get this

    ./httpd
    on_establish_callback({x: {data: (0x1007098f0 as *())}})

and the program waits for a connection.

If we run it and then use a web browser to connect to 127.0.0.1:3534 we get this (output slightly re-formatted for clarity)

    ./httpd
    on_establish_callback({x: {data: (0x10070a470 as *())}})
    new_connection_callback(NewTcpConn((0x100832c00 as *())), {x: {data: (0x10070a470 as *())}})
    rust: task failed at 'Now what ?', httpd.rc:34
    Assertion failed: (false && "Rust task failed after reentering the Rust stack"), \
        function upcall_call_shim_on_rust_stack, \
        file /Users/simon/Src/lang/rust-0.6/src/rt/rust_upcall.cpp, line 92.
    Abort trap

and the program exits.

4.0 Defining Some Simple Constants

At the moment we have a couple of integer constants sitting in the middle of the code which as everyone knows is bad, bad, bad.

We can fix this by defining them as constants like so

    ...

    static BACKLOG: uint = 5;
    static PORT:    uint = 3534;
    
    ...

These are both examples of a static_item.

Like other items, static_items are processed at compile time and the values they define are stored in the program’s static memory.

We also have a string literal in the middle of the code.

We can define it as a constant like this

    static IPV4_LOOPBACK: &'static str = "127.0.0.1";

In fact the string literal is already a constant stored in static memory.

What we are defining here is a borrowed pointer.

The construct

   'static

is a named lifetime and it tells the compiler how long we want to borrow the pointer for.[1]

5.0 The Source Code For httpd v0.1

    // httpd.rc

    // v 0.1

    extern mod std;

    use core::task;

    use std::net::ip;
    use std::net::tcp;

    use std::uv_iotask;

    static BACKLOG: uint = 5;
    static PORT:    uint = 3534;
    
    static IPV4_LOOPBACK: &'static str = "127.0.0.1";

    fn main()
    {	
        tcp::listen(
            ip::v4::parse_addr(IPV4_LOOPBACK),
            PORT,
            BACKLOG,
            &uv_iotask::spawn_iotask(task::task()),
            |chan|
            {
                io::println(fmt!("on_establish_callback(%?)", chan));
            },
            |newConn, chan|
            {
                io::println(fmt!("new_connection_callback(%?, %?)", newConn, chan));
                fail!(~"Now what ?");
            });
    }

Notes

  1. Given the context I am not sure why the compiler cannot infer the required lifetime, but it doesn’t, so you have to tell it or it gets upset.


Copyright (c) 2013 By Simon Lewis. All Rights Reserved.

Unauthorized use and/or duplication of this material without express and written permission from this blog’s author and owner Simon Lewis is strictly prohibited.

Excerpts and links may be used, provided that full and clear credit is given to Simon Lewis and justanapplication.wordpress.com with appropriate and specific direction to the original content.

June 11, 2013

Programming With Rust — Part Three: Things We Need To Know – Modules

1.0 Modules

A Rust module defines both a namespace and an access-control boundary.

In this example

    mod fish
    {
        enum FlatFish
        {
            Dab,
            Halibut,
            Flounder,
            Sole,
            Turbot
        }
    }

the type FlatFish could be referenced from within the fish module using the identifier FlatFish.

However it is not visible outside the module.

2.0 Access Control

To make it visible it is necessary to make it public it like this.

    mod fish
    {
        pub enum FlatFish
        {
            Dab,
            Halibut,
            Flounder,
            Sole,
            Turbot
        }
    }

The FlatFish type can now be referenced from other modules.

3.0 Paths

Now that the FlatFish type is public it can be referenced from outside the fish module using its path, that is, its fully qualified name, which is

    fish::FlatFish

4.0 Name Binding

Alternatively the name FlatFish can be bound locally in another module like this

    use fish::FlatFish;

Now the local name FlatFish can be used to reference the name FlatFish in the fish module.

5.0 Nested Modules

Modules can be nested.

For example

    mod underwater_creatures
    {
        mod fish
        {
            pub enum FlatFish
            {
                Dab,
                Halibut,
                Flounder,
                Sole,
                Turbot
            }
        }
    }

The path of the FlatFish type is, as you might expect

    underwater_creatures::fish::FlatFish

and to bind the name FlatFish locally you do this

    use underwater_creatures::fish::FlatFish;

6.0 Binding Module Names

You can also bind the name of a module locally, but visibility constraints apply to modules as well.

Given the example above you cannot do this

    use underwater_creatures::fish;

because the fish module is not public.

If the fish module is made public

    mod underwater_creatures
    {
        pub mod fish
        {
            pub enum FlatFish
            {
                Dab,
                Halibut,
                Flounder,
                Sole,
                Turbot
            }
        }
    }

then you can do this

    use underwater_creatures::fish;

and you can then refer to the FlatFish type using the path

    fish::FlatFish

This is useful if you want to use a prefix to qualify a name either to avoid name clashes and/or to identify where the name is from, but you do not want to use its path because of its length.

7.0 Aliasing

A name can be bound to an alias like this

    use ff = fish::FlatFish;

Now the local name ff can be used to reference the name FlatFish in the fish module.

8.0 Access Control And Name Bindings

By default name bindings are not visible outside the module in which they occur, but they can be made public in the same way
as enum types and modules.

The effect of doing this is that the local name is now visible outside the module and it in turn can now be referenced using its path and bound to a local name in another module and so on.

For example, if we do this

    mod underwater_creatures
    {
        pub use underwater_creatures::fish::FlatFish;
    
        mod fish
        {
            pub enum FlatFish
            {
                Dab,
                Flounder,
                Halibut,
                Sole,
                Turbot
            }
        }
    }

then you can use the path

   underwater_creatures::FlatFish

to reference the FlatFish type.

Alternatively you can bind the name locally like this

   use underwater_creatures::FlatFish

and the local name FlatFish will reference the FlatFish type.

Using aliasing in conjunction with a public name binding it is possible to re-name things in very confusing ways if you choose to do so.

For example,

    mod underwater_creatures
    {
        pub use FlatFish = underwater_creatures::fish::OtherFish;
    
        mod fish
        {
            pub enum FlatFish
            {
                Dab,
                Flounder,
                Halibut,
                Sole,
                Turbot
            }
        
            pub enum OtherFish
            {
                Perch,
                Roach,
                Tench
            }
        }
    }

Now the path

   underwater_creatures::FlatFish

references the OtherFish type in the fish module.


Copyright (c) 2013 By Simon Lewis. All Rights Reserved.

Unauthorized use and/or duplication of this material without express and written permission from this blog’s author and owner Simon Lewis is strictly prohibited.

Excerpts and links may be used, provided that full and clear credit is given to Simon Lewis and justanapplication.wordpress.com with appropriate and specific direction to the original content.

Create a free website or blog at WordPress.com.

%d bloggers like this: