rust: py03 how to get reference to struct member [duplicate] - rust

This question already has answers here:
Return reference to member field in PyO3
(2 answers)
Closed 7 months ago.
How do I edit Header.a via Packet.Header.a?
#![allow(dead_code)]
use pyo3::prelude::*;
#[pyclass]
#[derive(Clone)]
pub struct Header {
#[pyo3(get, set)]
a: u32,
#[pyo3(get, set)]
b: u32,
}
#[pymethods]
impl Header {
#[new]
fn new(a: u32, b: u32) -> Self {
Header { a, b }
}
}
#[pyclass]
/// Structure used to hold an ordered list of headers
pub struct Packet {
#[pyo3(get, set)]
pub h: Header,
}
#[pymethods]
impl Packet {
#[new]
fn new() -> Self {
Packet {
h: Header { a: 0, b: 0 },
}
}
}
#[pymodule]
fn pyo3test(_py: Python, m: &PyModule) -> PyResult<()> {
m.add_class::<Header>()?;
m.add_class::<Packet>()?;
Ok(())
}
After running "maturin develop", within python
from pyo3test import *
p = Packet()
print(p.h.a) # prints 0
h = p.h
h.a = 1
print(h.a) -> # prints 1
print(p.h.a) -> # still prints 0
p.h.a = 1
print(p.h.a) # still prints 0
This seems against python semantics. h is a reference to p.h. An update to h should have updated p.h.
How do I implement the get trait to return a reference to Packet.Header?

Disclaimer: I'm not an expert on PyO3. I just somewhat have an understanding of how Rust and Python work. So take everything I say with a grain of salt.
The problem here is that Rust and Python have a very different memory model. While Rust is ownership based, Python is reference-counted. This creates some challenges in implementing classes that are usable in both languages.
Specifically for getters/setters, it seems that PyO3 decided to clone() instead of reference counting:
For get the field type must implement both IntoPy<PyObject> and Clone.
There are further incompatibilities: Python is not typesafe. That means that any item could be of any type, for example, you could write:
p.h.a = "Test", although the definition Header::a in Rust is clearly a u32. This also "seems to be against python semantics".
That said, you could achieve something similar by using reference counters internally. You cannot, however (at the time of writing and from what I could tell) expose those reference counters to Python. (Meaning, you cannot use something like Arc<Header> and return this from a getter)
But you could make Header itself somewhat of a reference counter:
use std::sync::{
atomic::{AtomicU32, Ordering},
Arc,
};
use pyo3::prelude::*;
#[pyclass]
#[derive(Clone)]
pub struct Header {
a: Arc<AtomicU32>,
b: Arc<AtomicU32>,
}
#[pymethods]
impl Header {
#[new]
fn new(a: u32, b: u32) -> Self {
Header {
a: Arc::new(AtomicU32::new(a)),
b: Arc::new(AtomicU32::new(b)),
}
}
#[getter]
fn get_a(&self) -> PyResult<u32> {
Ok(self.a.load(Ordering::Acquire))
}
#[setter]
fn set_a(&mut self, value: u32) -> PyResult<()> {
self.a.store(value, Ordering::Release);
Ok(())
}
#[getter]
fn get_b(&self) -> PyResult<u32> {
Ok(self.b.load(Ordering::Acquire))
}
#[setter]
fn set_b(&mut self, value: u32) -> PyResult<()> {
self.b.store(value, Ordering::Release);
Ok(())
}
}
#[pyclass]
/// Structure used to hold an ordered list of headers
pub struct Packet {
#[pyo3(get, set)]
pub h: Header,
}
#[pymethods]
impl Packet {
#[new]
fn new() -> Self {
Packet {
h: Header::new(0, 0),
}
}
}
/// A Python module implemented in Rust.
#[pymodule]
#[pyo3(name = "rust_python_test")]
fn rust_python_test(_py: Python, m: &PyModule) -> PyResult<()> {
m.add_class::<Header>()?;
m.add_class::<Packet>()?;
Ok(())
}
#!/usr/bin/env python3
from rust_python_test import Packet
p = Packet()
print(p.h.a) # prints 0
h = p.h
h.a = 1
print(h.a) # prints 1
print(p.h.a) # prints 1
p.h.a = 1
print(p.h.a) # prints 1

Related

Rust: error: "returns a value referencing data owned by the current function"

I think this is a kind of common questions. I've read some solutions but my situation is a bit different...
It complains at line 8. However, I can't change the function signature of new_b as well as everything below the common line. They are all external packages.
So how can I design the function new_a to workaround?
fn main() {
new_a();
}
fn new_a() -> A<'static> {
let b = B {};
A { c: new_b(&b) }
}
pub struct A<'a> {
c: C<'a>,
}
// Below are exteral packages
fn new_b<'a>(b: &'a B) -> C<'a> {
C { b: &b }
}
pub struct B {}
pub struct C<'a> {
b: &'a B,
}
https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=160e63f1300506472b7c1b0811b37453

Implementing a dynamic-typed LinkedList in Rust

This is a follow-up on the question asked here: Possible to implement dynamically-typed linked list in safe Rust?
I successfully implemented a dynamic type LinkedList using the std::any::Any trait.
However, I want to challenge myself by trying to implement it in another way, e.g. using generic type - Node where T can be any type, u32, u64, String, ...
Example
Node<String> -> Node<u32> -> Node<u64> -> Node<String> -> ...
My approach is to use a trait called Next to give Node<T> the ability to "go next".
Node<T> looks like this.
struct Node<T> {
data: T,
next: Option<Rc<RefCell<dyn Next>>>,
}
The trait Next looks like this.
pub trait Next {
fn borrow_next(&self) -> Option<Ref<dyn Next>>;
fn set_next(&mut self, next: Rc<RefCell<dyn Next>>);
}
These are the implementation of Next for any Node.
impl<T> Next for Node<T> {
fn set_next(&mut self, next: Rc<RefCell<dyn Next>>) {
self.next = Some(next);
}
fn borrow_next(&self) -> Option<Ref<dyn Next>> {
match &self.next {
None => None,
Some(stmt) => Some(stmt.borrow()),
}
}
}
Here are the implementations for the actual struct Node<T>.
impl<T> Node<T> {
pub fn new<P>(data: P) -> Node<P> {
Node::<P> { data, next: None }
}
pub fn new_wrapped<P>(data: P) -> Rc<RefCell<Node<P>>> {
Rc::new(RefCell::new(Node::<P>::new(data)))
}
pub fn into_wrapped(self) -> Rc<RefCell<Self>> {
Rc::new(RefCell::new(self))
}
pub fn borrow_data(&self) -> &T {
&self.data
}
pub fn set_data(&mut self, data: T) {
self.data = data;
}
}
Lastly, the declaration and its implementations of methods of struct DynLinkedList, holding two fields, head and tail, look like this.
struct DynLinkedList {
head: Option<Rc<RefCell<dyn Next>>>,
tail: Option<Rc<RefCell<dyn Next>>>,
}
impl DynLinkedList {
pub fn new_empty() -> Self {
Self {
head: None,
tail: None,
}
}
pub fn new_with_node(node: Rc<RefCell<dyn Next>>) -> Self {
Self {
head: Some(node.clone()),
tail: Some(node),
}
}
pub fn append(&mut self, node: Rc<RefCell<dyn Next>>) {
self.tail.take().map_or_else(
|| self.head = Some(node.clone()),
|old_tail| old_tail.borrow_mut().set_next(node.clone()),
);
self.tail = Some(node);
}
}
Here comes the problem:
I am unable to access the data field of Node<T> as it is being treated as a trait object dyn Next by the compiler.
For example, this test would not work:
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_dynll_new_with_node() {
let node = Node::<u32>::new(77_u32);
let dynll = DynLinkedList::new_with_node(node.into_wrapped());
assert_eq!(&dynll.head.unwrap().borrow().borrow_data(), &77);
assert_eq!(&dynll.tail.unwrap().borrow().borrow_data(), &77)
}
}
The compiler error is:
error[E0599]: no method named `borrow_data` found for struct `Ref<'_, (dyn Next + 'static)>` in the current scope
--> src/dyn_ll_idea_five.rs:125:47
|
125 | assert_eq!(&*dynll.head.unwrap().borrow().borrow_data(), &77);
| ^^^^^^^^^^^ method not found in `Ref<'_, (dyn Next + 'static)>`
But, when the .borrow() after .unwrap() returns, it should return an object of type Node which would have the method .borrow_data(), how can I let Rust know that this is the case? Thank you.
I would effectively want to be able to do this:
let mut list = DynLinkedList::new();
list.push_front("hello".to_string());
list.push_back("world".to_string());
list.push_front(123);
list.push_back(456);
assert_eq!(list.pop_front(), Some("hello".to_string()));
assert_eq!(list.pop_back(), Some("world".to_string()));
assert_eq!(list.pop_front(), Some(123));
assert_eq!(list.pop_back(), Some(456));
Well, nowhere in the definition of trait Next does it talk about objects of type Node. Thus, how would the compiler ever know that you can call borrow_data on it? That's where you'd do the downcast via the Any trait.
What's more, the compiler would also want to know which sort of Node we're talking about. Node<i32> or Node<String> or what? And that's downright impossible because your list is dynamic and hence whatever type is contained within a node is also dynamic.
Let's take your example:
Node<String> -> Node<u32> -> Node<u64> -> Node<String> -> ...
So if that's your list, then, using very rough ugly pseudocode, what about this:
let x: String = my_list.head.borrow_data();
let y: u32 = my_list.head.next.borrow_data();
let z: u64 = my_list.head.next.next.borrow_data();
You see the problem here? How is the compiler to know, at compile time, that the third item in the list has type u64? This just isn't a case where generics work in the way you want it.

Why can I write Guess{value:1000} when value is private?

I have a code like this from the offical tutorial book Chapter 9.3
#![allow(unused)]
fn main() {
pub struct Guess {
value: i32,
}
impl Guess {
pub fn new(value: i32) -> Guess {
if value < 1 || value > 100 {
panic!("Guess value must be between 1 and 100, got {}.", value);
}
Guess { value }
}
pub fn value(&self) -> i32 {
self.value
}
}
let g = Guess{
value:1000
};
println!("{}", g.value);
}
According to the book I should not be able to create a Guess using the let g = Guess {} However, this code does not cause any error and prints 1000
There is still a problem even if I put the struct and impl outside the main func like this and delete the pub keyword.
struct Guess {}
impl Guess {}
fn main() {}
According to the book I should not be able to create a Guess using the let g = Guess {}
It is unlikely that the book states that.
Rust visibility works in terms of modules, anything in a module can see all the contents of their parents regardless of their pub status; the reverse is not true.
Since main and Guess.value are in the same module, there is no barrier. Moving Guess out of main doesn't change that, they're still in the same module. For visibility to become an issue, Guess needs to be moved into a separate non-ancestor module (e.g. a sub-module, or a sibling).
Example:
pub struct Foo(usize);
pub mod x {
pub struct Bar(usize);
impl Bar {
pub fn get(&self) -> usize { self.0 }
}
pub fn make(n: super::Foo) -> Bar {
// can access Foo's fields because this is a
// descendant module, so it is "part of" the
// same module
Bar(n.0)
}
}
pub fn qux(n: x::Bar) -> Foo {
// Can't access Bar's field because it's not exposed to this module
// Foo(n.0)
Foo(n.get())
}

How to store a field that only take reference to String created in `::new()`?

In the below code, how should I store a B field in A? Supposed that I can not change the definition of B.
As a newbie to Rust, I searched Google and most of them explain why the error happens. I could understand the compile error, but I can't figure out how to fix it. Or is it bad design to store the field in this case? Are there other solutions to this?
Thanks!
use std::io::Read;
struct A<'a> {
b: B<'a>,
}
impl<'a> A<'a> {
pub fn new(mut reader: impl Read) -> Self {
let mut s = String::new();
reader.read_to_string(&mut s);
let b = B { b: &s };
A { b }
}
}
// From 3rd party library, can't change the struct!
struct B<'a> {
b: &'a str,
}
fn main() {}
Well, in this case you need something to own the data. So just make your A to own it, and then have a method that creates the B type from A:
struct A {
s: String,
}
// From 3rd party library, can't change the struct!
struct B<'a> {
b: &'a str,
}
impl A {
pub fn new(mut reader: impl Read) -> Self {
let mut s = String::new();
reader.read_to_string(&mut s);
A { s }
}
fn as_b(&self) -> B {
B { b: &self.s }
}
}
Playground

Dealing with problematic parent-child relationships enforced by C FFI

I have a C library with an interface similar to this: (I have represented the C API within Rust, so that all of the code in this question can be concatenated in a single .rs file and easily tested)
// Opaque handles to C structs
struct c_A {}
struct c_B {}
// These 2 `create` functions allocate some heap memory and other
// resources, so I have represented this using Boxes.
extern "C" fn create_a() -> *mut c_A {
let a = Box::new(c_A {});
Box::into_raw(a)
}
// This C FFI function frees some memory and other resources,
// so I have emulated that here.
extern "C" fn destroy_a(a: *mut c_A) {
let _a: Box<c_A> = unsafe { Box::from_raw(a) };
}
extern "C" fn create_b(_a: *mut c_A) -> *mut c_B {
let b = Box::new(c_B {});
Box::into_raw(b)
}
// Note: While unused here, the argument `_a` is actually used in
// the C library, so I cannot remove it. (Also, I don't control
// the C interface)
extern "C" fn destroy_b(_a: *mut c_A, b: *mut c_B) {
let _b = unsafe { Box::from_raw(b) };
}
I have created the following Rusty abstraction over the C functions:
struct A {
a_ptr: *mut c_A,
}
impl A {
fn new() -> A {
A { a_ptr: create_a() }
}
}
impl Drop for A {
fn drop(&mut self) {
destroy_a(self.a_ptr);
}
}
struct B<'a> {
b_ptr: *mut c_B,
a: &'a A,
}
impl<'a> B<'a> {
fn new(a: &'a A) -> B {
B {
b_ptr: create_b(a.a_ptr),
a,
}
}
}
impl<'a> Drop for B<'a> {
fn drop(&mut self) {
destroy_b(self.a.a_ptr, self.b_ptr);
}
}
The B struct contains a reference to A for the sole reason that the a_ptr is necessary when calling the destroy_b function for memory cleanup. This reference is not needed by me for any of my Rust code.
I would now like to create the following struct which references both A and B:
struct C<'b> {
a: A,
b: B<'b>,
}
impl<'b> C<'b> {
fn new() -> C<'b> {
let a = A::new();
let b = B::new(&a);
C { a, b }
}
}
// Main function just so it compiles
fn main() {
let c = C::new();
}
However, this will not compile:
error[E0597]: `a` does not live long enough
--> src/main.rs:76:25
|
76 | let b = B::new(&a);
| ^ borrowed value does not live long enough
77 | C { a, b }
78 | }
| - borrowed value only lives until here
|
note: borrowed value must be valid for the lifetime 'b as defined on the impl at 73:1...
--> src/main.rs:73:1
|
73 | impl<'b> C<'b> {
| ^^^^^^^^^^^^^^
I understand why this fails: When returning the C struct from C::new(), it moves the C. This means that the A contained within is moved, which renders all references to it invalid. Therefore, there's no way I could create that C struct. (Explained in much more detail here)
How can I refactor this code in such a way that I can store a B in a struct along with its "parent" A? There's a few options I've thought of, that won't work:
Change the C interface: I don't control the C interface, so I can't change it.
Have B store a *mut c_A instead of &A: If A is dropped, then that raw pointer becomes invalid, and will result in undefined behavior when B is dropped.
Have B store an owned A rather than a reference &A: For my use case, I need to be able to create multiple Bs for each A. If B owns A, then each A can only be used to create one B.
Have A own all instances of B, and only return references to B when creating a new B: This has the problem that Bs will accumulate over time until the A is dropped, using up more memory than necessary. However, if this is indeed the best way to go about it, I can deal with the slight inconvenience.
Use the rental crate: I would rather take the slight memory usage hit than add the complexity of a new macro to my code. (That is, the complexity of anyone reading my code needing to learn how this macro works)
I suspect that the best solution somehow involves storing at least A on the heap so it doesn't need to move around, but I can't figure out how to make this work. Also, I wonder if there is something clever that I can do using raw pointers.
This sounds like an ideal case for reference counting. Use Rc or Arc, depending on your multithreading needs:
use std::rc::Rc;
struct B {
b_ptr: *mut c_B,
a: Rc<A>,
}
impl B {
fn new(a: Rc<A>) -> B {
B {
b_ptr: create_b(a.a_ptr),
a,
}
}
}
impl Drop for B {
fn drop(&mut self) {
destroy_b(self.a.a_ptr, self.b_ptr);
}
}
fn main() {
let a = Rc::new(A::new());
let x = B::new(a.clone());
let y = B::new(a);
}
Does not change the C interface.
A cannot be dropped while there are still Bs referencing it.
Can create multiple Bs for each A.
A's memory usage will not increase forever.
Creates a single heap allocation to store A and its reference count.
Rc is in the standard library, no new crate to learn.
In the future, you'll be able to use arbitrary self types to write this in a nicer manner:
#![feature(arbitrary_self_types)]
use std::rc::Rc;
struct A {
a_ptr: *mut c_A,
}
impl A {
fn new() -> A {
A { a_ptr: create_a() }
}
fn make_b(self: &Rc<Self>) -> B {
B {
b_ptr: create_b(self.a_ptr),
a: self.clone(),
}
}
}
impl Drop for A {
fn drop(&mut self) {
destroy_a(self.a_ptr);
}
}
struct B {
b_ptr: *mut c_B,
a: Rc<A>,
}
impl Drop for B {
fn drop(&mut self) {
destroy_b(self.a.a_ptr, self.b_ptr);
}
}
fn main() {
let a = Rc::new(A::new());
let x = a.make_b();
let y = a.make_b();
}

Resources