Rust functions that accept and return tuples

C has no notion of tuples, but the closest analog is a plain struct. You will need to create individual structs for each unique combination of types. Here, we create a structure that represents two 32-bit unsigned integers.

use std::convert::From;

// A Rust function that accepts a tuple
fn flip_things_around_rust(tup: (u32, u32)) -> (u32, u32) {
    let (a, b) = tup;
    (b + 1, a - 1)
}

// A struct that can be passed between C and Rust
#[repr(C)]
pub struct Tuple {
    x: u32,
    y: u32,
}

// Conversion functions
impl From<(u32, u32)> for Tuple {
    fn from(tup: (u32, u32)) -> Tuple {
        Tuple { x: tup.0, y: tup.1 }
    }
}

impl From<Tuple> for (u32, u32) {
    fn from(tup: Tuple) -> (u32, u32) {
        (tup.x, tup.y)
    }
}

// The exported C method
#[no_mangle]
pub extern "C" fn flip_things_around(tup: Tuple) -> Tuple {
    flip_things_around_rust(tup.into()).into()
}

#[repr(C)] is used to inform the compiler that it should arrange the fields of the struct as a C compiler would. The two conversion implementations use std::convert::From to provide ergonomic conversion between the struct and a corresponding tuple.

C

#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>

typedef struct {
  uint32_t x;
  uint32_t y;
} tuple_t;

extern tuple_t
flip_things_around(tuple_t);

int main(void) {
  tuple_t initial = { .x = 10, .y = 20 };
  tuple_t result = flip_things_around(initial);
  printf("(%" PRIu32 ",%" PRIu32 ")\n", result.x, result.y);
}

Since we are conforming to C-compatible idioms, the implementation is straight-forward. We define a struct with fields that match the types and order of the Rust struct, then create an instance and call the method.

Ruby

require 'ffi'

class Tuple < FFI::Struct
  layout :x, :uint32,
         :y, :uint32

  def to_s
    "(#{self[:x]},#{self[:y]})"
  end
end

module Tuples
  extend FFI::Library
  ffi_lib 'tuples'
  attach_function :flip_things_around, [Tuple.by_value], Tuple.by_value
end

tup = Tuple.new
tup[:x] = 10
tup[:y] = 20

puts Tuples.flip_things_around(tup)

To mirror the structure definition, we create a subclass of FFI::Struct and use layout to specify the field names and types.

When attaching the function, we use by_value to indicate that the struct will be passed directly, without the need for indirection via pointers.

Python

#!/usr/bin/env python3

import sys, ctypes
from ctypes import c_uint32, Structure

class Tuple(Structure):
    _fields_ = [("x", c_uint32),
                ("y", c_uint32)]

    def __str__(self):
        return "({},{})".format(self.x, self.y)

prefix = {'win32': ''}.get(sys.platform, 'lib')
extension = {'darwin': '.dylib', 'win32': '.dll'}.get(sys.platform, '.so')
lib = ctypes.cdll.LoadLibrary(prefix + "tuples" + extension)

lib.flip_things_around.argtypes = (Tuple, )
lib.flip_things_around.restype = Tuple

tup = Tuple(10, 20)

print(lib.flip_things_around(tup))

To mirror the structure definition, we create a subclass of ctypes.Structure and use _fields_ to specify the field names and types.

Haskell

Unfortunately, Haskell does not currently support passing or returning arbitrary structs. Pointer indirection is always required.

Node.js

const ffi = require('ffi-napi');
const ref = require('ref-napi');
const struct = require('ref-struct-di')(ref);

const Tuple = struct({
  x: 'uint32',
  y: 'uint32',
});

const lib = ffi.Library('libtuples', {
  flip_things_around: [Tuple, [Tuple]],
});

const tup = new Tuple({x: 10, y: 20});
const result = lib.flip_things_around(tup);
console.log('(%d,%d)', result.x, result.y);

The ref-struct-di package allows us to build struct types which can be passed to FFI functions.

C#

using System;
using System.Runtime.InteropServices;

[StructLayout(LayoutKind.Sequential)]
struct IntTuple {
    public uint x;
    public uint y;

    public static implicit operator Tuple<uint, uint>(IntTuple t)
    {
        return Tuple.Create(t.x, t.y);
    }

    public static implicit operator IntTuple(Tuple<uint, uint> t)
    {
        return new IntTuple { x = t.Item1, y = t.Item2 };
    }
};

class Tuples
{
    [DllImport("tuples")]
    private static extern IntTuple flip_things_around(IntTuple t);

    public static Tuple<uint, uint> FlipThingsAround(Tuple<uint, uint> t)
    {
        return flip_things_around(t);
    }

    static public void Main()
    {
        var tuple = Tuple.Create(10u, 20u);
        var newTuple = Tuples.FlipThingsAround(tuple);
        Console.WriteLine($"({newTuple.Item1},{newTuple.Item2})");
    }
}

To mirror the tuple structure definition, we create a struct using the StructLayout property and define the layout as sequential. We also provide some implicit conversion operators to make going between types fairly seamless.

Julia

#!/usr/bin/env julia
using Libdl

libname = "tuples"
if !Sys.iswindows()
    libname = "lib$(libname)"
end

lib = Libdl.dlopen(libname)
flipthingsaround_sym = Libdl.dlsym(lib, :flip_things_around)

struct Tuple
    x::UInt32
    y::UInt32
end

flipthingsaround(t:: Tuple) = ccall(
    flipthingsaround_sym,
    Tuple, (Tuple,),
    t)

initial = Tuple(10, 20)
newtuple = flipthingsaround(initial)
println("($(newtuple.x),$(newtuple.y))")

Julia struct types defined with the exact same field layout are already compatible with C’s data arrangement. Since all fields are isbits, then so is the Tuple type. As such, it will store each member inline and will be passed to the native function by value.