Continuo da qui l’esame della serie di post di TheK3nger sull’uso di Rust all’interno di codice Python e viceversa; oggi in particolare copio da How to use Rust in Python (Part 3).
Inoltre –per comodità– il Git è qui: THeK3nger/rust-python-integration-examples.
[W]e have seen how to pass not trivial data to Rust functions such as a Python list. It is still not enough, though. In many cases we need to pass complex data structure back and forth from a Rust library. We may need to pass quaternions [davvero qualcuno usa quella roba lì?], 3D points, trees, a list of “books”… In short: anything.
Lato Rust
As usual, the first thing to do is to implement the Rust library function. This time we need to design first a struct that will represent our complex object. We chose to represent 2D points.
#[repr(C)]
pub struct Point {
x: f64,
y: f64,
}
This is a classic rust struct declaration, but we need to use #[repr(C)]
to explain to the compiler that Point
is a C struct.
Now, we can simply implement the desired function. In this example we will implement a function that, given two Point
representing a segment, returns the midpoint of the segment.
#[no_mangle]
pub extern fn middle(p1: Point, p2: Point) -> Point {
Point { x: (p1.x + p2.x)/2.0, y: (p1.y + p2.y)/2.0 }
}
Lato Python
Python side is slightly more complicated. Also in Python, we need first to declare the Point
.
class Point(ctypes.Structure):
_fields_ = [("x", ctypes.c_double), ("y", ctypes.c_double)]
def __str__(self):
return "Point ({},{})".format(self.x, self.y)
The class representing our C/Rust struct is different from the usual one:
- The class must extend the
ctypes.Structure
class defined inctypes
. - The fields of the C struct must be defined using the builtin
_fields_ attribute
. This attribute must contain a list of tuples. Each tuple must contain the 1) name of the field and 2) the type of the field according the ctypes declaration. In our casePoint
has two fields, “x
” and “y
”, both doubles.
Now it is time to setup our Rust function. We specify the type declaration for our function.
lib.middle.argtypes = (Point, Point)
lib.middle.restype = Point
Then we can use this function as usual!
p1 = Point(1.0, 5.0)
p2 = Point(1.0, 10.0)
res_point = lib.middle(p1, p2)
print(res_point)
And we have done. It is not so difficult, isn’t it? Certo, semplicissimo, adesso provo, ecco il file rp_points.py
:
import ctypes
lib = ctypes.CDLL("./target/release/librustypython.so")
class Point(ctypes.Structure):
_fields_ = [("x", ctypes.c_double), ("y", ctypes.c_double)]
def __str__(self):
return "Point ({},{})".format(self.x, self.y)
# Then we specify as usual the type declaration of the Rust function.
lib.middle.argtypes = (Point, Point)
lib.middle.restype = Point
# And then we can easily use it as a native python function!
p1 = Point(1.0, 5.0)
p2 = Point(1.0, 10.0)
res_point = lib.middle(p1, p2)
print(res_point)
OK 😀 come già detto TheK3nger rockz! 😀 Rust & Python too 😀
Devo davvero imparare bene Rust, da usarsi per le operazioni impegnative.
Trackback
[…] limitato. E (rigarda me) Rust è troppo difficile (ho anche copiato TheK3nger, qui e qui ma non ho convinto). E i linguaggi funzionali (Lisp e tutti gli altri) no, solo per me. Ma in […]
[…] Uhmmm… oltre che molto specifico l’argomento è già stato trattato in modo comprensibile anche agli umani come me, copiando TheKeng3r, qui e qui. […]