Open15
[実験]PyO3を使ってPythonにRustの型を導入してみる
![Yos_K](https://zenn-dev.github.io/default-avatars/dark/s.png)
Option型を作ってみる
構造体を用意して、Option型をラップするだけ
#[pyclass(name="Option")]
#[derive(Debug, FromPyObject)]
pub struct RsOption {
pub value: Option<PyObject>
}
#[pymethods]
impl RsOption {
#[new]
pub fn new(n: Option<PyObject>) -> Self {
Self { value: n }
}
#[getter]
fn value(&self) -> PyResult<Option<PyObject>> {
Ok(self.value.clone())
}
}
![Yos_K](https://zenn-dev.github.io/default-avatars/dark/s.png)
Option型のメソッドを実装する
基本的に内部でOption型の同名のメソッドを呼び出すだけだが、Pythonから関数を受け取って適用する場合は工夫がいる(たぶん)
pub const fn is_some(&self) -> bool {
self.value.is_some()
}
pub fn is_some_and(&self, f: PyObject) -> bool {
match &self.value {
None => false,
Some(x) => {
Python::with_gil(|py| {
f.call1(py, (x,)).map(|r| r.to_object(py)).unwrap()
.downcast_bound::<PyBool>(py).unwrap().extract().unwrap()
})
},
}
}
![Yos_K](https://zenn-dev.github.io/default-avatars/dark/s.png)
Python側でprintできるように__str__を実装する
もう少しいい書き方がありそうな気がする
fn __str__(&self) -> String {
let s = Python::with_gil(|py| {
self.value().unwrap().to_object(py).to_string()
});
if &s != "None" {
format!("Some({})", &s)
} else {
s
}
}
![Yos_K](https://zenn-dev.github.io/default-avatars/dark/s.png)
Python側ではスタブファイルをこんなかんじで用意しておく
from typing import Callable, Tuple, TypeVar, Generic
T = TypeVar('T')
U = TypeVar('U')
E = TypeVar('E')
class Option(Generic[T]):
def __init__(obj: T) -> None: ...
@property
def value(self) -> T: ...
def is_some(self) -> bool: ...
def is_some_and(self, f: Callable[[T], bool]) -> bool: ...
def is_none(self) -> bool: ...
def expect(self, msg: str) -> T: ...
def unwrap(self) -> T: ...
def unwrap_or(self, default: T) -> T: ...
def unwrap_or_else(self, f: Callable[[], T]) -> T: ...
def map(self, f: Callable[[T], U]) -> Option[U]: ...
def inspect(self, f: Callable[[T],]) -> Option: ...
def map_or(self, default: U, f: Callable[[T], U]) -> U: ...
def map_or_else(self, default: Callable[[], U], f: Callable[[T], U]) -> U: ...
def ok_or(self, err: E) -> Result[T, E]: ...
def ok_or_else(self, err: Callable[[], E]) -> Result[T, E]: ...
def and_then(self, f: Callable[[T], Option[U]]) -> Option[U]: ...
def or_else(self, f: Callable[[], Option[T]]) -> Option[T]: ...
def zip(self, other: Option[U]) -> Option[Tuple[T, U]]: ...
![Yos_K](https://zenn-dev.github.io/default-avatars/dark/s.png)
こんなかんじで型を推論してくれる
![Yos_K](https://zenn-dev.github.io/default-avatars/dark/s.png)
Resultも同様に実装してみる
#[pyclass(name="Result")]
#[derive(Debug, FromPyObject)]
pub enum RsResult {
Ok {value: PyObject},
Err {value: PyObject}
}
#[pymethods]
impl RsResult {
fn __str__(&self) -> String {
match &self {
RsResult::Ok { value } => format!("Ok({})", &value),
RsResult::Err { value } => format!("Err({})", &value),
}
}
}
![Yos_K](https://zenn-dev.github.io/default-avatars/dark/s.png)
stubは冗長になるけどこんなかんじに書くと良さそう
class Result:
class Ok(Generic[T]):
def __init__(self, value: T) -> None: ...
def is_ok(self) -> bool: ...
def is_ok_and(self, f: Callable[[T], bool]) -> bool: ...
def is_err(self) -> bool: ...
def is_err_and(self, f: Callable[[T], bool]) -> bool: ...
def ok(self) -> Option[T]: ...
def err(self) -> Option[E]: ...
def map(self, f: Callable[[T], U]) -> Result.Ok[U]: ...
def map_or(self, default: U, f: Callable[[T], U]) -> U: ...
def map_or_else(self, default: Callable[[], U], f: Callable[[T], U]) -> U: ...
class Err(Generic[T]):
def __init__(self, value: T) -> None: ...
def is_ok(self) -> bool: ...
def is_ok_and(self, f: Callable[[T], bool]) -> bool: ...
def is_err(self) -> bool: ...
def is_err_and(self, f: Callable[[T], bool]) -> bool: ...
def ok(self) -> Option[T]: ...
def err(self) -> Option[E]: ...
def map(self, f: Callable[[T], U]) -> Result.Err[U]: ...
def map_or(self, default: U, f: Callable[[T], U]) -> U: ...
def map_or_else(self, default: Callable[[], U], f: Callable[[T], U]) -> U: ...
def map_err(self, op: Callable[[T], U]) -> Result[U]: ...
![Yos_K](https://zenn-dev.github.io/default-avatars/dark/s.png)
型を推論させるとこんなかんじ
![Yos_K](https://zenn-dev.github.io/default-avatars/dark/s.png)
stubがごちゃついてきたので整理
↑を参考に![Yos_K](https://zenn-dev.github.io/default-avatars/dark/s.png)
実装したOptionのメソッド
class Option(Generic[T]):
def __init__(self, obj: T) -> None: ...
@property
def value(self) -> T: ...
def is_some(self) -> bool: ...
def is_some_and(self, f: Callable[[T], bool]) -> bool: ...
def is_none(self) -> bool: ...
def expect(self, msg: str) -> T: ...
def unwrap(self) -> T: ...
def unwrap_or(self, default: T) -> T: ...
def unwrap_or_else(self, f: Callable[[], T]) -> T: ...
def map(self, f: Callable[[T], U]) -> Option[U]: ...
def inspect(self, f: Callable[[T],]) -> Option: ...
def map_or(self, default: U, f: Callable[[T], U]) -> U: ...
def map_or_else(self, default: Callable[[], U], f: Callable[[T], U]) -> U: ...
def ok_or(self, err: E) -> Result: ...
def ok_or_else(self, err: Callable[[], E]) -> Result: ...
def and_then(self, f: Callable[[T], Option[U]]) -> Option[U]: ...
def or_else(self, f: Callable[[], Option[T]]) -> Option[T]: ...
def zip(self, other: Option[U]) -> Option[Tuple[T, U]]: ...
def transpose(self) -> Result[Option[T]]: ...
def flatten(self) -> Option[T]: ...
![Yos_K](https://zenn-dev.github.io/default-avatars/dark/s.png)
Resultのメソッド
class Result:
class Ok(Generic[T]):
def __init__(self, value: T) -> None: ...
def is_ok(self) -> bool: ...
def is_ok_and(self, f: Callable[[T], bool]) -> bool: ...
def is_err(self) -> bool: ...
def is_err_and(self, f: Callable[[T], bool]) -> bool: ...
def ok(self) -> Option[T]: ...
def err(self) -> Option[E]: ...
def map(self, f: Callable[[T], U]) -> Result.Ok[U]: ...
def map_or(self, default: U, f: Callable[[T], U]) -> U: ...
def map_or_else(self, default: Callable[[], U], f: Callable[[T], U]) -> U: ...
def inspect(self, f: Callable[[T],]) -> Result: ...
def inspect_err(self, f: Callable[[T],]) -> Result: ...
def expect(self, msg: str) -> T: ...
def unwrap(self) -> T: ...
def expect_err(self, msg: str) -> T: ...
def unwrap_err(self) -> T: ...
def and_then(self, op: Callable[[T], Result]) -> Result: ...
def or_else(self, op: Callable[[T], Result]) -> Result: ...
def unwrap_or(self, default: T) -> T: ...
def unwrap_or_else(self, op: Callable[[E], T]) -> T: ...
def transpose(self) -> Option[Result.Ok[T]]: ...
def flatten(self) -> Result.Ok[T]: ...
class Err(Generic[T]):
def __init__(self, value: T) -> None: ...
def is_ok(self) -> bool: ...
def is_ok_and(self, f: Callable[[T], bool]) -> bool: ...
def is_err(self) -> bool: ...
def is_err_and(self, f: Callable[[T], bool]) -> bool: ...
def ok(self) -> Option[T]: ...
def err(self) -> Option[E]: ...
def map(self, f: Callable[[T], U]) -> Result.Err[U]: ...
def map_or(self, default: U, f: Callable[[T], U]) -> U: ...
def map_or_else(self, default: Callable[[], U], f: Callable[[T], U]) -> U: ...
def map_err(self, op: Callable[[T], U]) -> Result: ...
def inspect(self, f: Callable[[T],]) -> Result: ...
def inspect_err(self, f: Callable[[T],]) -> Result: ...
def expect(self, msg: str) -> T: ...
def unwrap(self) -> T: ...
def expect_err(self, msg: str) -> T: ...
def unwrap_err(self) -> T: ...
def and_then(self, op: Callable[[T], Result]) -> Result: ...
def or_else(self, op: Callable[[T], Result]) -> Result: ...
def unwrap_or(self, default: T) -> T: ...
def unwrap_or_else(self, op: Callable[[E], T]) -> T: ...
def transpose(self) -> Option[Result.Err[T]]: ...
def flatten(self) -> Result.Err[T]: ...