Skip to Content
Technical Articles

Rust bindings for the SAP NW RFC library

Rust is a programming language from the Mozilla foundation that aims to achieve “C”-like performance while providing safety guarantees like a managed or scripted language, such as ABAP or Java, and a type system as powerful as haskell‘s, but without any performance penalties or even a garbage collector. Rusts achieves this by providing zero cost abstractions and a novel approach to memory handling called the “borrow checker“: Each allocated piece of memory has a designated owner, and others can borrow references, but only as long as the owner of the reference lives. Rust’s compiler’s type checker ensures this constraint is not violated at compliation time.

Our company is doing some SAP development in rust and we have decided to release our bindings to the SAP NW RFC library as free software, meaning it is now easy to call into an SAP NetWeaver system from the rust language!

Please note that you will still need to manually download the SAP NW RFC library from SAP to use our library. We simply provide the bindings to rust.

Let me in the following show a simple example.

To connect to an SAP system via RFC it is required to fill out some connection parameters. This is how it’s done in rust:

    let conn_params = RfcConnectionParameters {
        ashost: "10.8.0.18",
        sysnr: "00",
        client: "001",
        user: "bobpage",
        passwd: "Maggie8+Chow%-!",
        lang: "EN",
    };

Next, we need to establish the actual connection:

    let conn = RfcConnection::new(&conn_params)?;

The error handling is done implicitly by using the ? operator at the end of the statement: If the call returns an error, then the calling function is terminated and the error propagated.

After the connection to the SAP system is established, we need to access an RFC enabled function module:

    let mut rfc_read_table = conn.get_function("RFC_READ_TABLE")?;

Note again the use of the ? operator. If the function module does not exist, is not RFC enabled, or the calling user has no permissions to access the function module’s metadata, an error is returned, which we propagate by the use of ?. Note also the mut keyword: This is indicating to rust that we want to mutate the state of the function reference we have just received. This is required so we can set some parameters before calling the function.

Suppose we wish to fetch a list of all users of an SAP system (please do this only on your own development system 😉 We can use the RFC function RFC_READ_TABLE to facilitate this. We just need to tell it which table and columns we are interested in. We are interested in one column BNAME of the table USR02.

First, we set the name of the table, USR02:

    {
        let query_table = rfc_read_table
            .get_mut_parameter("QUERY_TABLE")
            .ok_or(RfcErrorInfo::custom("unknown field QUERY_TABLE"))?;
        query_table.set_string("USR02")?;
    }

Note the scoping block { }. This is necessary because we borrow a reference of rfc_read_table here, which we need to release before we can borrow another reference mutably. This is because rust only allows one mutable reference to be borrowed at any time.

Next, we need to tell RFC_READ_TABLE we are interested in the column BNAME:

    {
        let fields = rfc_read_table.get_mut_parameter("FIELDS")
            .ok_or(RfcErrorInfo::custom("unknown field FIELDNAME"))?;
        let idx_fieldname = fields
            .get_field_index_by_name("FIELDNAME")?;
        fields.append_rows(1)?;
        fields.first_row()?;
        let fieldname = fields
            .get_field_by_index_mut(idx_fieldname)?;
        fieldname
            .set_string("BNAME")?;
    }

This is a bit involved. Let’s step through it line by line. First, we obtain a mutable reference to the parameter FIELDS. We need to release that reference before we can call the function, so again, we need to introduce a block { } here.

Then, we get the numerical index of the parameter FIELDNAME of the table parameter FIELDS. Next, we append a row to that table parameter and set the column FIELDNAME to the value BNAME.

Now we’re set to call the function:

    rfc_read_table.call()?;

After all the hard work done before, the actual invocation of the function is straightforward. Have you noticed that nearly all the calls we’ve made to the RFC function library can fail? Thus, we use the ? operator quite often.

If the function returnes successfully, rfc_read_table.call() will return Ok(()) and the flow of the program continues. Next step: Retrieve and print the results:

    let data = rfc_read_table.get_mut_parameter("DATA")
        .ok_or(RfcErrorInfo::custom("unknown field DATA"))?;
    let idx_wa = data.get_field_index_by_name("WA")?;
    let num_users = data.get_row_count()?;

First, we get a reference to the table parameter DATA. Then, we get  the numerical index for the DATA table’s WA field. We also retrieve the number of columns we have received from SAP. Now we need to iterate over all rows and fetch the contents of the WA column and print it:

    for i in 0..num_users {
        data.set_row(i)?;
        let row_content = data
            .get_field_by_index(idx_wa)?
            .get_chars()?;
        println!("Username: {}", row_content.trim_end());
    }

That’s it! Successful call from rust to SAP. You may have noticed that no .close() calls happen. This is done implicitly whenever a variable goes out of scope. Also, the rust borrow checker ensures you cannot pass around a reference to an RFC function of a closed connection. This both prevents surprises and also lets you find some logic erros in your code early. (Of course, this does not protect you against network failures, emergency reboots, random bitflips or bugs in the rust compiler (or indeed, our library))

Speaking of which, note please that our library is a work in progress and still lacks some features. For example, it is not possible yet to write an RFC server.

Thanks for reading! I hope I could motivate you a little to have a look at rust, which is a fascinating language, or perhaps even to have a closer look into the inner workings of the SAP RFC protocol? 🙂

Check out our source code at https://github.com/CaiberP/rsrfc; you will also find the complete source code of the example I gave in this article. Pull requests, bug reports and comments are always welcome!

1 Comment
You must be Logged on to comment or reply to a post.
  • Hi Hans-Christian,

    Thanks for sharing! Delighted to see another Rust fan in the SAP Community. Could you perhaps add a user tag of Rust to the blog post?

    Cheers

    Andrew