Rust 函数:接受并返回元组

C 没有元组的概念,但最接近的模拟是一个普通的结构。 您需要为每种独特的类型组合,创建单独的结构。在这里,我们创建一个结构,它表示两个 32 位无符号整数。

extern crate libc;

use libc::uint32_t;
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: uint32_t,
    y: uint32_t,
}

// 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 fn flip_things_around(tup: Tuple) -> Tuple {
    flip_things_around_rust(tup.into()).into()
}

#[allow(dead_code)]
pub extern fn fix_linking_when_not_using_stdlib() { panic!() }

#[repr(C)]用于通知编译器,它应该像 C 编译器那样排列结构的字段。在结构和相应的元组之间的转换实现,都用到了std::convert::From trait,提供符合人体工程学的转换操作。

C

#include <stdio.h>
#include <stdint.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 new = flip_things_around(initial);
  printf("(%u,%u)\n", new.x, new.y);
  return 0;
}

由于我们符合 C 兼容的习语,因此实现是直截了当的。我们定义一个struct,使用与 Rust 结构的类型和顺序匹配的字段,然后创建一个实例并调用该方法。

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)

为了效仿结构定义,我们创建了FFI::Struct的一个子类,并使用layout指定字段名称和类型。

函数附加(attach_function)时,我们使用by_value表示结构是直接传递的,而不需要通过指针进行间接传递。

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))

为了效仿结构定义,我们创建了一个ctypes.Structure子类,并使用_fields_指定字段名称和类型。

Haskell

不幸的是,Haskell 目前不支持,传递或返回任意结构。始终需要指针间接。

Node.js

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

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);

ref-struct允许我们,构建可以传递给 FFI 函数 的结构类型。

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})");
    }
}

为了效仿 元组结构定义,我们创建了一个struct,使用StructLayout属性,并将布局定义为顺序sequential。 我们还提供了一些隐式转换操作,使类型之间的转换无缝连接。

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 结构类型,已与 C 的数据编排兼容。由于所有字段都是isbits,之后自然是Tuple类型。这是因为,它存储每个内联内容,并将按值传递给原生函数。