8

I'm new to Rust and PyO3 (coming from Python) so this might be obvious to more experienced people.

I declared a pyclass struct in PyO3.

#[pyclass]
struct Block {
    start: i32,
    stop: i32,
}

Then I use Block in a rust function that takes a vector of Block and outputs a vector of int (signature below)

#[pyfunction]
fn from_blocks(block_list: Vec<Block>) -> Vec<i32>

When I compile using nightly-x86_64-apple-darwin I get the following error:

#[pyfunction]
^^^^^^^^^^^^^ the trait `pyo3::FromPyObject<'_>` is not implemented for `std::vec::Vec<Block>`

How do I solve this?

EDIT: Caio is right. I made a mistake in tracing back the error. Previously I wrote

Then I use Block in a rust function that takes a vector of int and outputs a vector of Block (signature below)

#[pyfunction]
fn to_blocks(list: Vec<i32>) -> Vec<Block>

But the actual offending function is:

#[pyfunction]
fn from_blocks(block_list: Vec<Block>) -> Vec<i32>

I've updated the question to make it clearer.

kentwait
  • 1,969
  • 2
  • 21
  • 42
  • 1
    Sorry to confuse everyone. @Caio intuitively figured out that the offending function has the reverse signature. I've updated the question as a result. – kentwait Jan 16 '19 at 16:49

3 Answers3

5

FromPyObject is intended to be used by types that can be extracted from the Python world. That is why I think that you were trying to write fn to_blocks(list: Vec<Block>) -> Vec<i32> instead of fn to_blocks(list: Vec<i32>) -> Vec<Block>. If that is the case , lets go down to the implementation chain.

FromPyObject has a default implementation for any &T that implements PyTryFrom and PyTryFrom has a default implementation for any T that implements PyTypeInfo. [pyclass] implements PyObjectAlloc or PyObjectWithFreeList according to the impl_class method and both traits have the PyTypeInfo trait bound. Therefore, your class/struct will work just fine with references, e.g.:

#[pyfunction]
fn to_blocks(list: Vec<&Block>) -> Vec<i32>

You can see in the official documentation this explanation in a summarized way.

FromPyObject is implemented by various types that can be extracted from a Python object reference.

Caio
  • 3,178
  • 6
  • 37
  • 52
  • Why would a function named `to_blocks` take a vector of blocks as input and return a vector of integers? – interjay Jan 16 '19 at 14:07
  • Despite the name, `fn to_blocks(list: Vec) -> Vec` compiles without issues and even if it didn't compile, the error would be different. – Caio Jan 16 '19 at 14:54
  • I don't understand your comment. If it compiles without issues then why fix it? In any case, it doesn't make sense to suggest swapping the function's inputs and outputs as that wouldn't give OP what they need. – interjay Jan 16 '19 at 15:45
  • 1
    The compile error he gave in this question simply relates to the fact that custom derives aren't enough to enable a local owned type to be used in a function parameter. That is why I **suggested** that he **could** have written the wrong code and **if** that were the case, the above measures of this answer would be necessary. If I wasn't clear enough I can edit my post in a proper way. – Caio Jan 16 '19 at 16:30
  • Are you saying that the code he actually compiled is not the code shown in the question? If so, saying "your code should be" is confusing because it sounds like you're suggesting this would fix the problem. – interjay Jan 16 '19 at 16:34
  • 1
    @interjay, @caio apologies for the confusion. I think @caio figured out that I since I have a `fn to_blocks` then there's an opposite doing the reverse and that's what's giving the error. – kentwait Jan 16 '19 at 16:52
  • Thank you kentwait. @interjay made some important statements that are now fixed. – Caio Jan 16 '19 at 17:02
  • pyo3 documentation still thin on this. It took quite a lot of googling to find this thread. Thanks – paddyg Dec 13 '19 at 22:58
0

It looks like the pyfunction attribute generates code that requires that the return type implements the FromPyObject trait. While there's a blanket implementation of FromPyObject for Vec<T> where T: FromPyObject, it looks like the code generated for pyclass attribute does not include an implementation of FromPyObject for your Block type.

Since I am unfamiliar with PyO3 other than the few minutes I just looked at its API documentation to validate this answer, I'm not sure how you'd best get a FromPyObject implementation -- perhaps there's a derive for it?

djc
  • 11,603
  • 5
  • 41
  • 54
0

Which version of PyO3 are you on? Your code is working for me on 0.5.3 and 0.6.0-alpha.1.

Due to that I can't test this, but I would guess that you need to return a PyResult:

#[pyfunction]
fn to_blocks(list: Vec<i32>) -> PyResult<Vec<Block>>
Chronial
  • 66,706
  • 14
  • 93
  • 99
  • 1
    `#[pyclass]` implements `IntoPyObject`/`ToPyObject` for `Block`, that is why it works. OP code should probably be `fn to_blocks(list: Vec) -> Vec`. – Caio Jan 16 '19 at 15:28
  • @Caio I'm confused – since OP's code works, why should it be something else? – Chronial Jan 16 '19 at 15:43
  • Because the error message he gave is the same error message one gets when `Block` is used as an input parameter. To be sure, I think it is best to wait his response about this matter. – Caio Jan 16 '19 at 15:59
  • 1
    @Caio you are right, I traced the error back to the wrong function. The error stems from `fn from_blocks(block_list: Vec) -> Vec` – kentwait Jan 16 '19 at 16:47
  • @kentwait Thank you =) – Caio Jan 16 '19 at 16:53