2015-06-11 16 views
9

Tôi có hàm Rust trả về array và tôi muốn sử dụng mảng này trong Python, có thể là list hoặc numpy.array nó không thực sự quan trọng.Sử dụng mảng Rust được trả về bằng Python sử dụng ctypes

chức năng Rust của tôi trông như thế này:

#[no_mangle] 
pub extern fn make_array() -> [i32; 4] { 
    let my_array: [i32; 4] = [1,2,3,4]; 
    return my_array; 
} 

Và tôi đang cố gắng để gọi nó bằng Python như thế này:

In [20]: import ctypes 

In [21]: from ctypes import cdll 

In [22]: lib = cdll.LoadLibrary("/home/user/RustStuff/embed/target/release/libembed.so") 

In [23]: lib.make_array.restype = ctypes.ARRAY(ctypes.c_int32, 4) 

In [24]: temp = lib.make_array() 

In [25]: [i for i in temp] 
Out[25]: [1, 2, -760202930, 32611] 

Tôi đang làm gì sai? Tại sao đầu ra của tôi không phải là [1,2,3,4]? Tại sao hai yếu tố đầu tiên của tôi đúng và hai yếu tố khác lại có rác thải?

Tôi không thể tìm thấy bất kỳ tài liệu nào tốt trên ctypes.ARRAY, vì vậy tôi chỉ cần đi với những gì nhìn đúng, do đó có thể là vấn đề.

+1

Nit kiểu nhỏ, nhưng hàm Rust của bạn có thể là 'pub extern fn make_array() -> [i32 ; 4] {[1,2,3,4]} '. – Shepmaster

+0

@Shepmaster, tôi thấy cảm ơn bạn. Tôi cần phải quen dần với phong cách 'rỉ sét'. – Akavall

+1

Tôi không biết ABI của Rust xử lý các mảng có kích thước cố định như thế nào nhưng AFAIK C không thể làm điều đó vì vậy tôi không mong đợi ctypes, hoặc bất kỳ người tiêu dùng C ABI nào, có thể làm việc với điều đó. – delnan

Trả lời

5

Như những người khác đã nói, bạn không thể thực sự trả về một mảng có kích thước cố định đúng cách. Nhưng bạn có thể lừa ctypes vào làm đúng bằng cách gói các mảng trong một cấu trúc:

import ctypes 

class Int32_4(ctypes.Structure): 
    _fields_ = [("array", ctypes.c_int32 * 4)] 

lib = ctypes.CDLL("embed.dll") 
lib.make_array.restype = Int32_4 

temp = lib.make_array() 

print(temp.array[:]) 

này kết quả trong [1, 2, 3, 4] trên máy tính của tôi.

Phụ lục: Đây là một mẹo "" vì chúng tôi đang khai thác sự khác biệt giữa những gì C có thể làm và những gì Rust có thể làm. C sẽ không cho phép bạn trả lại một mảng có kích thước cố định theo giá trị, nhưng Rust sẽ và nó hoạt động giống như trả lại cấu trúc do người dùng xác định.

Vì vậy, chúng tôi thực hiện điều gì đó mà C sẽ cho phép: trả lại cấu trúc có chứa một mảng có kích thước cố định. Điều này, nó là tốt với, và nó phù hợp với cách bố trí mà Rust đang sử dụng.

Tất nhiên, điều này cũng hơi khó hiểu, trong đó tôi không phải là hoàn toàn đã thuyết phục rằng đây là hành vi được xác định rõ.Nếu bạn muốn an toàn hơn, bạn có thể thay đổi loại trả về ở mặt Rust để khớp với C:

#[repr(C)] 
struct Int32_4 { 
    array: [i32; 4] 
} 
+0

Tốt, nhưng bạn có thể giải thích cách hoạt động của giải pháp của bạn. Ví dụ, tôi không hiểu tại sao đây là một "thủ thuật" trái ngược với cách sử dụng bình thường của 'ctypes'. – Akavall

+0

Làm cho cảm giác hoàn hảo ngay bây giờ. Cảm ơn bạn. – Akavall

+0

@DK .: Bố trí trong bộ nhớ sẽ khớp vì C Standard yêu cầu địa chỉ của 'struct' và địa chỉ của thành viên dữ liệu đầu tiên khớp với nó (tức là không có đệm trước); nó có thể đang chạy theo các quy tắc bí mật nghiêm ngặt, nhưng qua các ngôn ngữ, nó không quan trọng. Tuy nhiên, tốt hơn là bọc trong 'struct' trên cả hai đầu, nó sẽ dẫn đến ít trầy xước hơn trên một phần của các nhà bảo trì trong tương lai. –

1

Tôi đồng ý với những gì @delnan cho biết - you can't return fixed-size arrays in C. Một không tương thích chính là mảng Rust biết kích thước của chúng là gì, nhưng mảng C thì không. Bạn sẽ cần phải tuân thủ như thế nào mỗi chương trình C khác đã làm điều này - trả về một con trỏ và một chiều dài riêng biệt. Không phải là một ngôn ngữ hiện đại đẹp đẽ trong so sánh?

tôi lấy trộm và sửa đổi một số mã Python from another answer

import ctypes 

from ctypes import cdll 

lib = cdll.LoadLibrary("libarray.dylib") 
lib.make_array.restype = ctypes.POINTER(ctypes.c_int32 * 4) 

print [i for i in lib.make_array().contents] 

này hoạt động với mã Rust này:

static ARRAY: [i32; 4] = [1,2,3,4]; 

#[no_mangle] 
pub extern fn make_array() -> *const i32 { 
    ARRAY.as_ptr() 
} 

Ở đây, chúng tôi đang làm những điều đơn giản nhất, tạo ra một mảng mà sẽ sống cho toàn bộ chiều dài của chương trình và trả về một tham chiếu đến dữ liệu của nó. Trong chương trình thực sự của bạn, bạn có thể cần phải cẩn thận hơn để đảm bảo rằng Vec<i32> hoặc &[i32] các ngoại lệ nghiêm ngặt của bạn bao lâu mã Python có con trỏ, nếu không bạn sẽ gây ra tham nhũng bộ nhớ.

+0

Với kết quả của tôi, có vẻ như con trỏ đến phần tử đầu tiên thực sự được truyền, vì hai phần tử đầu tiên là đúng, và đó là độ dài bị tắt! – Akavall

+0

@Akavall Tôi không biết bố cục bộ nhớ của mảng Rust là gì, vì vậy thật khó đoán được điều gì đang xảy ra. Một số trong số đó có thể là các byte đệm chưa được khởi tạo. Có thể kích thước của mảng là bất kỳ kích thước nào từ 'u8' đến' u64' (hoặc có thể 'sử dụng'), do đó không dễ giải mã bộ nhớ bạn đang in. – Shepmaster

+0

OK, tôi hiểu. Cảm ơn. – Akavall

Các vấn đề liên quan