代码之家  ›  专栏  ›  技术社区  ›  ItsTimmy

处理外国金融机构实施的有问题的亲子关系

  •  8
  • ItsTimmy  · 技术社区  · 7 年前

    我有一个c库,它的接口类似于这样:(我在rust中表示了c api,所以这个问题中的所有代码都可以在一个 .rs 文件和易于测试)

    // 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) };
    }
    

    我在c函数上创建了以下生锈的抽象:

    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);
        }
    }
    

    这个 B 结构包含对 A 唯一的原因是 a_ptr 打电话给 destroy_b 用于内存清理的函数。我不需要这个参考资料来查我的任何生锈代码。

    我现在要创建以下同时引用a和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();
    }
    

    但是,这不会编译:

    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> {
       | ^^^^^^^^^^^^^^
    

    我明白为什么失败了:当返回 C 结构自 C::new() ,它移动 C类 是的。这意味着 一个 包含在中的将被移动,这将使对它的所有引用无效。因此,我无法创造 C类 结构。( Explained in much more detail here )

    如何重构此代码,以便能够存储 在结构及其“父”中 一个 是吗?我想了几个办法,都不行:

    • 更改C接口:我不控制C接口,因此无法更改它。
    • 拥有 存储A *mut c_A 而不是 &A :如果 一个 被删除,则原始指针将变为无效,并在以下情况下导致未定义的行为 被丢弃。
    • 拥有 存储所拥有的 一个 而不是参考 &一个 :对于我的用例,我需要能够创建多个 S代表每个 一个 . 如果 拥有 一个 ,则每个 一个 只能用于创建一个 .
    • 拥有 一个 拥有的所有实例 ,并且只返回对 创建新的 :问题是 s将随着时间的推移而累积,直到 一个 被丢弃,用光了多余的内存。不过,如果这确实是最好的办法,我可以处理轻微的不便。
    • 使用 rental 我宁愿接受轻微的内存使用命中,也不把新宏的复杂性添加到我的代码中。(即,阅读我的代码的人的复杂性,需要了解这个宏是如何工作的)

    我怀疑最好的解决方案至少包括存储 一个 所以不需要移动,但我不知道怎么做。此外,我想知道是否有什么聪明的事情,我可以做使用原始指针。

    1 回复  |  直到 7 年前
        1
  •  3
  •   Shepmaster Tim Diekmann    7 年前

    这听起来像是参考计数的理想案例。使用 Rc Arc ,具体取决于您的多线程需求:

    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);
    }
    
    • 不更改C接口。
    • A 无法在仍然存在的情况下丢弃 B 正在引用它。
    • 可以创建多个 S代表每个 一个 是的。
    • 一个 的内存使用不会永远增加。
    • 创建要存储的单个堆分配 一个 以及它的参考值。
    • 钢筋混凝土 是在标准图书馆,没有新的板条箱学习。

    在未来,你将能够使用 任意自我类型 以更好的方式写这篇文章:

    #![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();
    }
    
    推荐文章