简体   繁体   English

为什么我不能在同一结构中存储值和对该值的引用?

[英]Why can't I store a value and a reference to that value in the same struct?

I have a value and I want to store that value and a reference to something inside that value in my own type:我有一个值,我想在我自己的类型中存储该值和对该值内部某些内容的引用:

struct Thing {
    count: u32,
}

struct Combined<'a>(Thing, &'a u32);

fn make_combined<'a>() -> Combined<'a> {
    let thing = Thing { count: 42 };

    Combined(thing, &thing.count)
}

Sometimes, I have a value and I want to store that value and a reference to that value in the same structure:有时,我有一个值,我想将该值和对该值的引用存储在同一结构中:

struct Combined<'a>(Thing, &'a Thing);

fn make_combined<'a>() -> Combined<'a> {
    let thing = Thing::new();

    Combined(thing, &thing)
}

Sometimes, I'm not even taking a reference of the value and I get the same error:有时,我什至没有引用该值,我得到了同样的错误:

struct Combined<'a>(Parent, Child<'a>);

fn make_combined<'a>() -> Combined<'a> {
    let parent = Parent::new();
    let child = parent.child();

    Combined(parent, child)
}

In each of these cases, I get an error that one of the values "does not live long enough".在每种情况下,我都会收到一个错误,其中一个值“寿命不够长”。 What does this error mean?这个错误是什么意思?

Let's look at a simple implementation of this :让我们看一下这个的简单实现

struct Parent {
    count: u32,
}

struct Child<'a> {
    parent: &'a Parent,
}

struct Combined<'a> {
    parent: Parent,
    child: Child<'a>,
}

impl<'a> Combined<'a> {
    fn new() -> Self {
        let parent = Parent { count: 42 };
        let child = Child { parent: &parent };

        Combined { parent, child }
    }
}

fn main() {}

This will fail with the error:这将失败并出现错误:

error[E0515]: cannot return value referencing local variable `parent`
  --> src/main.rs:19:9
   |
17 |         let child = Child { parent: &parent };
   |                                     ------- `parent` is borrowed here
18 | 
19 |         Combined { parent, child }
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ returns a value referencing data owned by the current function

error[E0505]: cannot move out of `parent` because it is borrowed
  --> src/main.rs:19:20
   |
14 | impl<'a> Combined<'a> {
   |      -- lifetime `'a` defined here
...
17 |         let child = Child { parent: &parent };
   |                                     ------- borrow of `parent` occurs here
18 | 
19 |         Combined { parent, child }
   |         -----------^^^^^^---------
   |         |          |
   |         |          move out of `parent` occurs here
   |         returning this value requires that `parent` is borrowed for `'a`

To completely understand this error, you have to think about how the values are represented in memory and what happens when you move those values.要完全理解此错误,您必须考虑这些值在内存中的表示方式以及移动这些值时会发生什么。 Let's annotate Combined::new with some hypothetical memory addresses that show where values are located:让我们用一些假设的内存地址来注释Combined::new ,这些地址显示了值的位置:

let parent = Parent { count: 42 };
// `parent` lives at address 0x1000 and takes up 4 bytes
// The value of `parent` is 42 
let child = Child { parent: &parent };
// `child` lives at address 0x1010 and takes up 4 bytes
// The value of `child` is 0x1000
         
Combined { parent, child }
// The return value lives at address 0x2000 and takes up 8 bytes
// `parent` is moved to 0x2000
// `child` is ... ?

What should happen to child ? child应该怎么办? If the value was just moved like parent was, then it would refer to memory that no longer is guaranteed to have a valid value in it.如果该值只是像parent一样被移动,那么它将引用不再保证其中具有有效值的内存。 Any other piece of code is allowed to store values at memory address 0x1000.允许任何其他代码将值存储在内存地址 0x1000 处。 Accessing that memory assuming it was an integer could lead to crashes and/or security bugs, and is one of the main categories of errors that Rust prevents.假设它是一个整数来访问该内存可能会导致崩溃和/或安全错误,并且是 Rust 防止的主要错误类别之一。

This is exactly the problem that lifetimes prevent.这正是生命周期阻止的问题。 A lifetime is a bit of metadata that allows you and the compiler to know how long a value will be valid at its current memory location .生命周期是一些元数据,它允许您和编译器知道一个值在其当前内存位置有效的时间。 That's an important distinction, as it's a common mistake Rust newcomers make.这是一个重要的区别,因为这是 Rust 新手常犯的错误。 Rust lifetimes are not the time period between when an object is created and when it is destroyed! Rust 生命周期不是创建对象和销毁对象之间的时间段!

As an analogy, think of it this way: During a person's life, they will reside in many different locations, each with a distinct address.打个比方,这样想:在一个人的一生中,他们将居住在许多不同的地方,每个地方都有一个不同的地址。 A Rust lifetime is concerned with the address you currently reside at , not about whenever you will die in the future (although dying also changes your address). Rust 生命周期与您当前居住的地址有关,而不是您将来何时会死(尽管死亡也会改变您的地址)。 Every time you move it's relevant because your address is no longer valid.每次您移动它都是相关的,因为您的地址不再有效。

It's also important to note that lifetimes do not change your code;同样重要的是要注意生命周期不会改变您的代码。 your code controls the lifetimes, your lifetimes don't control the code.你的代码控制生命周期,你的生命周期不控制代码。 The pithy saying is "lifetimes are descriptive, not prescriptive".精辟的说法是“一生是描述性的,而不是规定性的”。

Let's annotate Combined::new with some line numbers which we will use to highlight lifetimes:让我们用一些行号来注释Combined::new ,我们将使用这些行号来突出生命周期:

{                                          // 0
    let parent = Parent { count: 42 };     // 1
    let child = Child { parent: &parent }; // 2
                                           // 3
    Combined { parent, child }             // 4
}                                          // 5

The concrete lifetime of parent is from 1 to 4, inclusive (which I'll represent as [1,4] ). parent具体生命周期是从 1 到 4,包括 1 到 4(我将表示为[1,4] )。 The concrete lifetime of child is [2,4] , and the concrete lifetime of the return value is [4,5] . child的具体生命周期是[2,4] ,返回值的具体生命周期是[4,5] It's possible to have concrete lifetimes that start at zero - that would represent the lifetime of a parameter to a function or something that existed outside of the block.有可能有从零开始的具体生命周期 - 这将代表函数参数的生命周期或存在于块之外的东西。

Note that the lifetime of child itself is [2,4] , but that it refers to a value with a lifetime of [1,4] .请注意, child本身的生命周期是[2,4] ,但它的是一个生命周期为[1,4]的值。 This is fine as long as the referring value becomes invalid before the referred-to value does.只要引用值在引用值之前变得无效,这很好。 The problem occurs when we try to return child from the block.当我们尝试从块中返回child时,就会出现问题。 This would "over-extend" the lifetime beyond its natural length.这将使寿命“过度延长”,超出其自然长度。

This new knowledge should explain the first two examples.这个新知识应该解释前两个例子。 The third one requires looking at the implementation of Parent::child .第三个需要查看Parent::child的实现。 Chances are, it will look something like this:很有可能,它看起来像这样:

impl Parent {
    fn child(&self) -> Child { /* ... */ }
}

This uses lifetime elision to avoid writing explicit generic lifetime parameters .这使用生命周期省略来避免编写显式的通用生命周期参数 It is equivalent to:它相当于:

impl Parent {
    fn child<'a>(&'a self) -> Child<'a> { /* ... */ }
}

In both cases, the method says that a Child structure will be returned that has been parameterized with the concrete lifetime of self .在这两种情况下,该方法都表示将返回一个已使用self的具体生命周期参数化的Child结构。 Said another way, the Child instance contains a reference to the Parent that created it, and thus cannot live longer than that Parent instance.换句话说, Child实例包含对创建它的Parent实例的引用,因此不能比Parent实例活得更长。

This also lets us recognize that something is really wrong with our creation function:这也让我们认识到我们的创建函数确实有问题:

fn make_combined<'a>() -> Combined<'a> { /* ... */ }

Although you are more likely to see this written in a different form:尽管您更有可能看到它以不同的形式写成:

impl<'a> Combined<'a> {
    fn new() -> Combined<'a> { /* ... */ }
}

In both cases, there is no lifetime parameter being provided via an argument.在这两种情况下,都没有通过参数提供生命周期参数。 This means that the lifetime that Combined will be parameterized with isn't constrained by anything - it can be whatever the caller wants it to be.这意味着Combined将被参数化的生命周期不受任何约束——它可以是调用者想要的任何东西。 This is nonsensical, because the caller could specify the 'static lifetime and there's no way to meet that condition.这是荒谬的,因为调用者可以指定'static生命周期并且没有办法满足该条件。

How do I fix it?我如何解决它?

The easiest and most recommended solution is to not attempt to put these items in the same structure together.最简单和最推荐的解决方案是不要尝试将这些项目放在同一个结构中。 By doing this, your structure nesting will mimic the lifetimes of your code.通过这样做,您的结构嵌套将模仿代码的生命周期。 Place types that own data into a structure together and then provide methods that allow you to get references or objects containing references as needed.将拥有数据的类型放在一个结构中,然后提供允许您根据需要获取引用或包含引用的对象的方法。

There is a special case where the lifetime tracking is overzealous: when you have something placed on the heap.有一种特殊情况是生命周期跟踪过于热心:当你有东西放在堆上时。 This occurs when you use a Box<T> , for example.例如,当您使用Box<T>时,就会发生这种情况。 In this case, the structure that is moved contains a pointer into the heap.在这种情况下,被移动的结构包含指向堆的指针。 The pointed-at value will remain stable, but the address of the pointer itself will move.指向的值将保持稳定,但指针本身的地址会移动。 In practice, this doesn't matter, as you always follow the pointer.在实践中,这并不重要,因为您总是跟随指针。

Some crates provide ways of representing this case, but they require that the base address never move .一些 crate 提供了表示这种情况的方法,但它们要求基地址永远不会移动 This rules out mutating vectors, which may cause a reallocation and a move of the heap-allocated values.这排除了变异向量,这可能导致堆分配值的重新分配和移动。

Examples of problems solved with Rental:使用租赁解决的问题示例:

In other cases, you may wish to move to some type of reference-counting, such as by using Rc or Arc .在其他情况下,您可能希望使用某种类型的引用计数,例如使用RcArc

More information更多信息

After moving parent into the struct, why is the compiler not able to get a new reference to parent and assign it to child in the struct?parent移入结构后,为什么编译器无法获取对parent级的新引用并将其分配给结构中的child级?

While it is theoretically possible to do this, doing so would introduce a large amount of complexity and overhead.虽然理论上可以做到这一点,但这样做会带来大量的复杂性和开销。 Every time that the object is moved, the compiler would need to insert code to "fix up" the reference.每次移动对象时,编译器都需要插入代码来“修复”引用。 This would mean that copying a struct is no longer a very cheap operation that just moves some bits around.这意味着复制结构不再是一个非常便宜的操作,只是移动一些位。 It could even mean that code like this is expensive, depending on how good a hypothetical optimizer would be:这甚至可能意味着这样的代码很昂贵,这取决于假设的优化器有多好:

let a = Object::new();
let b = a;
let c = b;

Instead of forcing this to happen for every move, the programmer gets to choose when this will happen by creating methods that will take the appropriate references only when you call them.程序员可以通过创建仅在您调用它们时才采用适当引用的方法来选择何时发生这种情况,而不是强制每次移动都发生这种情况。

A type with a reference to itself引用自身的类型

There's one specific case where you can create a type with a reference to itself.在一种特殊情况下,您可以创建一个引用自身的类型。 You need to use something like Option to make it in two steps though:不过,您需要使用Option之类的东西分两步完成:

#[derive(Debug)]
struct WhatAboutThis<'a> {
    name: String,
    nickname: Option<&'a str>,
}

fn main() {
    let mut tricky = WhatAboutThis {
        name: "Annabelle".to_string(),
        nickname: None,
    };
    tricky.nickname = Some(&tricky.name[..4]);

    println!("{:?}", tricky);
}

This does work, in some sense, but the created value is highly restricted - it can never be moved.从某种意义上说,这确实有效,但创造的价值受到高度限制——它永远无法移动。 Notably, this means it cannot be returned from a function or passed by-value to anything.值得注意的是,这意味着它不能从函数返回或按值传递给任何东西。 A constructor function shows the same problem with the lifetimes as above:构造函数显示了与上述生命周期相同的问题:

fn creator<'a>() -> WhatAboutThis<'a> { /* ... */ }

If you try to do this same code with a method, you'll need the alluring but ultimately useless &'a self .如果您尝试使用方法执行相同的代码,您将需要诱人但最终无用&'a self When that's involved, this code is even more restricted and you will get borrow-checker errors after the first method call:当涉及到这一点时,此代码会受到更多限制,并且在第一个方法调用后您将收到借用检查器错误:

#[derive(Debug)]
struct WhatAboutThis<'a> {
    name: String,
    nickname: Option<&'a str>,
}

impl<'a> WhatAboutThis<'a> {
    fn tie_the_knot(&'a mut self) {
       self.nickname = Some(&self.name[..4]); 
    }
}

fn main() {
    let mut tricky = WhatAboutThis {
        name: "Annabelle".to_string(),
        nickname: None,
    };
    tricky.tie_the_knot();

    // cannot borrow `tricky` as immutable because it is also borrowed as mutable
    // println!("{:?}", tricky);
}

See also:也可以看看:

What about Pin ? Pin呢?

Pin , stabilized in Rust 1.33, has this in the module documentation : Pin在 Rust 1.33 中稳定,在模块文档中有这个:

A prime example of such a scenario would be building self-referential structs, since moving an object with pointers to itself will invalidate them, which could cause undefined behavior.这种情况的一个主要示例是构建自引用结构,因为移动带有指向自身的指针的对象将使它们无效,这可能导致未定义的行为。

It's important to note that "self-referential" doesn't necessarily mean using a reference .需要注意的是,“自我引用”并不一定意味着使用引用 Indeed, the example of a self-referential struct specifically says (emphasis mine):确实, 自引用结构的示例特别说明了(强调我的):

We cannot inform the compiler about that with a normal reference, since this pattern cannot be described with the usual borrowing rules.我们无法通过正常的引用通知编译器,因为这种模式不能用通常的借用规则来描述。 Instead we use a raw pointer , though one which is known to not be null, since we know it's pointing at the string.相反,我们使用原始指针,尽管已知它不为空,因为我们知道它指向字符串。

The ability to use a raw pointer for this behavior has existed since Rust 1.0.自 Rust 1.0 以来,就已经存在使用原始指针来实现这种行为的能力。 Indeed, owning-ref and rental use raw pointers under the hood.实际上,owning-ref 和 rent 使用了底层的原始指针。

The only thing that Pin adds to the table is a common way to state that a given value is guaranteed to not move. Pin添加到表中的唯一内容是声明给定值保证不会移动的常用方法。

See also:也可以看看:

A slightly different issue which causes very similar compiler messages is object lifetime dependency, rather than storing an explicit reference.导致非常相似的编译器消息的一个稍微不同的问题是对象生存期依赖性,而不是存储显式引用。 An example of that is the ssh2 library.一个例子是ssh2库。 When developing something bigger than a test project, it is tempting to try to put the Session and Channel obtained from that session alongside each other into a struct, hiding the implementation details from the user.在开发比测试项目更大的东西时,很容易尝试将从该会话中获得的SessionChannel并排放入一个结构中,从而对用户隐藏实现细节。 However, note that the Channel definition has the 'sess lifetime in its type annotation, while Session doesn't.但是,请注意Channel定义在其类型注释中具有'sess生命周期,而Session没有。

This causes similar compiler errors related to lifetimes.这会导致与生命周期相关的类似编译器错误。

One way to solve it in a very simple way is to declare the Session outside in the caller, and then for annotate the reference within the struct with a lifetime, similar to the answer in this Rust User's Forum post talking about the same issue while encapsulating SFTP.以一种非常简单的方式解决它的一种方法是在调用者外部声明Session ,然后在结构中使用生命周期来注释引用,类似于这个 Rust 用户论坛帖子中的答案,在封装时讨论相同的问题SFTP。 This will not look elegant and may not always apply - because now you have two entities to deal with, rather than one that you wanted!这看起来并不优雅,并且可能并不总是适用 - 因为现在您有两个实体要处理,而不是您想要的一个!

Turns out the rental crate or the owning_ref crate from the other answer are the solutions for this issue too.事实证明,另一个答案中的出租箱owning_ref 箱也是这个问题的解决方案。 Let's consider the owning_ref, which has the special object for this exact purpose: OwningHandle .让我们考虑一下 owning_ref,它具有用于这个确切目的的特殊对象: OwningHandle To avoid the underlying object moving, we allocate it on the heap using a Box , which gives us the following possible solution:为了避免底层对象移动,我们使用Box在堆上分配它,这为我们提供了以下可能的解决方案:

use ssh2::{Channel, Error, Session};
use std::net::TcpStream;

use owning_ref::OwningHandle;

struct DeviceSSHConnection {
    tcp: TcpStream,
    channel: OwningHandle<Box<Session>, Box<Channel<'static>>>,
}

impl DeviceSSHConnection {
    fn new(targ: &str, c_user: &str, c_pass: &str) -> Self {
        use std::net::TcpStream;
        let mut session = Session::new().unwrap();
        let mut tcp = TcpStream::connect(targ).unwrap();

        session.handshake(&tcp).unwrap();
        session.set_timeout(5000);
        session.userauth_password(c_user, c_pass).unwrap();

        let mut sess = Box::new(session);
        let mut oref = OwningHandle::new_with_fn(
            sess,
            unsafe { |x| Box::new((*x).channel_session().unwrap()) },
        );

        oref.shell().unwrap();
        let ret = DeviceSSHConnection {
            tcp: tcp,
            channel: oref,
        };
        ret
    }
}

The result of this code is that we can not use the Session anymore, but it is stored alongside with the Channel which we will be using.这段代码的结果是我们不能再使用Session ,但它与我们将使用的Channel一起存储。 Because the OwningHandle object dereferences to Box , which dereferences to Channel , when storing it in a struct, we name it as such.因为OwningHandle对象取消引用Box ,后者取消引用Channel ,所以当将其存储在结构中时,我们将其命名为这样。 NOTE: This is just my understanding.注意:这只是我的理解。 I have a suspicion this may not be correct, since it appears to be quite close to discussion of OwningHandle unsafety .我怀疑这可能不正确,因为它似乎与OwningHandle的讨论非常接近。

One curious detail here is that the Session logically has a similar relationship with TcpStream as Channel has to Session , yet its ownership is not taken and there are no type annotations around doing so.这里一个奇怪的细节是Session在逻辑上与TcpStream具有类似的关系,就像ChannelSession一样,但它的所有权没有被占用,并且没有类型注释。 Instead, it is up to the user to take care of this, as the documentation of handshake method says:相反,这取决于用户,因为握手方法的文档说:

This session does not take ownership of the socket provided, it is recommended to ensure that the socket persists the lifetime of this session to ensure that communication is correctly performed.此会话不获取所提供套接字的所有权,建议确保套接字在此会话的生命周期内保持不变,以确保正确执行通信。

It is also highly recommended that the stream provided is not used concurrently elsewhere for the duration of this session as it may interfere with the protocol.还强烈建议在此会话期间不要在其他地方同时使用提供的流,因为它可能会干扰协议。

So with the TcpStream usage, is completely up to the programmer to ensure the correctness of the code.所以用TcpStream的用法,完全由程序员来保证代码的正确性。 With the OwningHandle , the attention to where the "dangerous magic" happens is drawn using the unsafe {} block.使用OwningHandle时,使用unsafe {}块可以引起对“危险魔法”发生位置的注意。

A further and a more high-level discussion of this issue is in this Rust User's Forum thread - which includes a different example and its solution using the rental crate, which does not contain unsafe blocks.在这个Rust 用户论坛线程中对这个问题进行了更深入和更高级的讨论 - 其中包括一个不同的示例及其使用不包含不安全块的出租箱的解决方案。

I've found the Arc (read-only) or Arc<Mutex> (read-write with locking) patterns to be sometimes quite useful tradeoff between performance and code complexity (mostly caused by lifetime-annotation).我发现Arc (只读)或Arc<Mutex> (带锁定的读写)模式有时在性能和代码复杂性(主要由生命周期注释引起)之间进行权衡非常有用。

Arc:弧:

use std::sync::Arc;

struct Parent {
    child: Arc<Child>,
}
struct Child {
    value: u32,
}
struct Combined(Parent, Arc<Child>);

fn main() {
    let parent = Parent { child: Arc::new(Child { value: 42 }) };
    let child = parent.child.clone();
    let combined = Combined(parent, child.clone());

    assert_eq!(combined.0.child.value, 42);
    assert_eq!(child.value, 42);
    // combined.0.child.value = 50; // fails, Arc is not DerefMut
}

Arc + Mutex:弧+互斥:

use std::sync::{Arc, Mutex};

struct Child {
    value: u32,
}
struct Parent {
    child: Arc<Mutex<Child>>,
}
struct Combined(Parent, Arc<Mutex<Child>>);

fn main() {
    let parent = Parent { child: Arc::new(Mutex::new(Child {value: 42 }))};
    let child = parent.child.clone();
    let combined = Combined(parent, child.clone());

    assert_eq!(combined.0.child.lock().unwrap().value, 42);
    assert_eq!(child.lock().unwrap().value, 42);
    child.lock().unwrap().value = 50;
    assert_eq!(combined.0.child.lock().unwrap().value, 50);
}

See also RwLock ( When or why should I use a Mutex over an RwLock? )另请参阅RwLock何时或为什么应该在 RwLock 上使用互斥锁?

As a newcomer to Rust, I had a case similar to your last example:作为 Rust 的新手,我有一个与您上一个示例类似的案例:

struct Combined<'a>(Parent, Child<'a>);

fn make_combined<'a>() -> Combined<'a> {
    let parent = Parent::new();
    let child = parent.child();

    Combined(parent, child)
}

In the end, I solved it by using this pattern:最后,我通过使用这种模式解决了它:

fn make_parent_and_child<'a>(anchor: &'a mut DataAnchorFor1<Parent>) -> Child<'a> {
    // construct parent, then store it in anchor object the caller gave us a mut-ref to
    *anchor = DataAnchorFor1::holding(Parent::new());

    // now retrieve parent from storage-slot we assigned to in the previous line
    let parent = anchor.val1.as_mut().unwrap();

    // now proceed with regular code, except returning only the child
    // (the parent can already be accessed by the caller through the anchor object)
    let child = parent.child();
    child
}

// this is a generic struct that we can define once, and use whenever we need this pattern
// (it can also be extended to have multiple slots, naturally)
struct DataAnchorFor1<T> {
    val1: Option<T>,
}
impl<T> DataAnchorFor1<T> {
    fn empty() -> Self {
        Self { val1: None }
    }
    fn holding(val1: T) -> Self {
        Self { val1: Some(val1) }
    }
}

// for my case, this was all I needed
fn main_simple() {
    let anchor = DataAnchorFor1::empty();
    let child = make_parent_and_child(&mut anchor);
    let child_processing_result = do_some_processing(child);
    println!("ChildProcessingResult:{}", child_processing_result);
}

// but if access to parent-data later on is required, you can use this
fn main_complex() {
    let anchor = DataAnchorFor1::empty();
    
    // if you want to use the parent object (which is stored in anchor), you must...
    // ...wrap the child-related processing in a new scope, so the mut-ref to anchor...
    // ...gets dropped at its end, letting us access anchor.val1 (the parent) directly
    let child_processing_result = {
        let child = make_parent_and_child(&mut anchor);
        // do the processing you want with the child here (avoiding ref-chain...
        // ...back to anchor-data, if you need to access parent-data afterward)
        do_some_processing(child)
    };

    // now that scope is ended, we can access parent data directly
    // so print out the relevant data for both parent and child (adjust to your case)
    let parent = anchor.val1.unwrap();
    println!("Parent:{} ChildProcessingResult:{}", parent, child_processing_result);
}

This is far from a universal solution!这远不是一个通用的解决方案! But it worked in my case, and only required usage of the main_simple pattern above (not the main_complex variant), because in my case the "parent" object was just something temporary (a database "Client" object) that I had to construct to pass to the "child" object (a database "Transaction" object) so I could run some database commands.但它在我的情况下有效,并且只需要使用上面的main_simple模式(不是main_complex变体),因为在我的情况下,“父”对象只是我必须构建的临时对象(数据库“客户端”对象)传递给“子”对象(数据库“事务”对象),以便我可以运行一些数据库命令。

Anyway, it accomplished the encapsulation/simplification-of-boilerplate that I needed (since I had many functions that needed creation of a Transaction/"child" object, and now all they need is that generic anchor-object creation line), while avoiding the need for using a whole new library.无论如何,它完成了我需要的封装/简化样板(因为我有许多需要创建事务/“子”对象的函数,现在他们需要的只是通用的锚对象创建行),同时避免需要使用一个全新的库。

These are the libraries I'm aware of that may be relevant:这些是我知道的可能相关的库:

However, I scanned through them, and they all seem to have issues of one kind or another (not being updated in years, having multiple unsoundness issues/concerns raised, etc.), so I was hesitant to use them.但是,我浏览了它们,它们似乎都有这样或那样的问题(多年没有更新,有多个不健全的问题/提出的担忧等),所以我犹豫要不要使用它们。

So while this isn't as generic of a solution, I figured I would mention it for people with similar use-cases:因此,虽然这不是一个通用的解决方案,但我想我会为有类似用例的人提及它:

  • Where the caller only needs the "child" object returned.调用者只需要返回的“子”对象。
  • But the called-function needs to construct a "parent" object to perform its functions.但是被调用函数需要构造一个“父”对象来执行其功能。
  • And the borrowing rules requires that the "parent" object be stored somewhere that persists beyond the "make_parent_and_child" function.借用规则要求“父”对象存储在“make_parent_and_child”函数之外的某个地方。 (in my case, this was a start_transaction function) (就我而言,这是一个start_transaction函数)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 为什么我不能通过可变值的可变引用的不可变引用来更新值? - Why can't I update a value through an immutable reference of a mutable reference of a mutable value? 为什么我不能重新绑定引用变量,但我可以重新绑定结构中的引用字段? - Why can't I rebind reference variable but I can rebind reference field in struct? 使用结构存储对非Copy值的引用 - Using a struct to store a reference to a non-Copy value 为什么我不能对一个永不超出 scope 的值进行 static 引用? - Why can't I make a static reference to a value that never goes out of scope? 为什么我可以返回对函数拥有值的引用? - Why can I return a reference to an owned value of a function? 为什么我可以通过值和参考来匹配 HashMap::get 的结果? - Why can I match on the result of HashMap::get by both value and reference? 如何将 Chars 迭代器存储在与它正在迭代的 String 相同的结构中? - How can I store a Chars iterator in the same struct as the String it is iterating on? 为什么我可以毫无错误地转换为另一个引用结构? - Why can I convert to another reference struct without error? 为什么我可以使用可变变量使其生命周期与不可变引用重叠,但我不能以相同的方式使用可变引用? - Why can I use a mutable variable such that its lifetime overlaps with an immutable reference, but I can't use a mutable reference in the same way? VSCode 调试无法显示参考值 - VSCode debug can't show reference value
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM