Rust 为什么可以';我在同一个结构中存储一个值和对该值的引用吗?

Rust 为什么可以';我在同一个结构中存储一个值和对该值的引用吗?,rust,reference,lifetime,borrow-checker,Rust,Reference,Lifetime,Borrow Checker,我有一个值,我想存储该值和对的引用 在我自己的类型中,该值内的某些内容: 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)

我有一个值,我想存储该值和对的引用 在我自己的类型中,该值内的某些内容:

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)
}
struct东西{
计数:u32,
}
结构组合{
让事物=事物{计数:42};
组合(对象和对象计数)
}
有时,我有一个值,我想存储该值和对的引用 相同结构中的该值:

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

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

    Combined(thing, &thing)
}
struct组合{
让事物=事物::新();
组合(事物和事物)
}
有时,我甚至没有引用值,我得到了 同样的错误:

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

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

    Combined(parent, child)
}
struct组合);
fn使联合{
让parent=parent::new();
让child=parent.child();
合并(父、子)
}
在每种情况下,我都会得到一个值“不”的错误 “活得不够长”。这个错误是什么意思?

让我们看看:

孩子应该怎么办?如果该值只是像父项那样移动 是的,那么它将引用不再保证可用的内存 其中包含有效值。允许存储任何其他代码段 内存地址0x1000处的值。假设它是 整数可能导致崩溃和/或安全漏洞,是其中之一 防止生锈的主要错误类别

这正是生命周期所防止的问题。一辈子就是一辈子 一点元数据,让您和编译器知道 值将在其当前内存位置有效。这是一个很好的例子 重要的区别,因为这是新手经常犯的错误。 生锈寿命不是指物体被腐蚀的时间间隔 当它被创建和销毁时

打个比方,这样想:在一个人的一生中,他们会 居住在许多不同的位置,每个位置都有不同的地址。A. Rust lifetime与您当前居住的地址有关, 不是关于你将来什么时候会死(虽然也会死) 更改您的地址)。每次你搬家都很重要,因为你的 地址不再有效

还需要注意的是,生命周期不会改变代码;你的 代码控制生命周期,你的生命周期不控制代码。这个 精辟的说法是“生命是描述性的,而不是规定性的”

让我们用一些行号注释
Combined::new
,我们将使用这些行号 要突出显示生命周期,请执行以下操作:

{                                          // 0
    let parent = Parent { count: 42 };     // 1
    let child = Child { parent: &parent }; // 2
                                           // 3
    Combined { parent, child }             // 4
}                                          // 5
parent
的具体生命周期是从1到4,包括在内(我将 表示为
[1,4]
)。
子对象的具体寿命为
[2,4]
,且 返回值的具体生存期为
[4,5]
。它是 可能会有从零开始的具体生命周期-这会 表示某个函数或某个 存在于街区之外

请注意,
子项
本身的生存期是
[2,4]
,但它指的是 到寿命为
[1,4]的值。这是好的,只要
引用的值在引用的值生效之前变为无效。这个
当我们试图从块中返回
子项时,会出现问题。这会
“过度延长”寿命超过其自然长度

这一新知识应该解释前两个例子。第三 我们需要查看
Parent::child
的实现。机会 是的,它看起来像这样:

impl Parent {
    fn child(&self) -> Child { /* ... */ }
}
它使用生存期省略来避免编写显式泛型 寿命参数。这相当于:

impl Parent {
    fn child<'a>(&'a self) -> Child<'a> { /* ... */ }
}
虽然您更可能看到这是以不同的形式编写的:

impl<'a> Combined<'a> {
    fn new() -> Combined<'a> { /* ... */ }
}
程序员可以通过创建只在您调用它们时才使用适当引用的方法来选择何时会发生这种情况,而不是强迫每次移动都发生这种情况

具有自身引用的类型 有一种特殊情况,您可以创建一个带有自身引用的类型。不过,您需要使用类似于
选项的方法来分两步实现:

#[派生(调试)]
结构是什么,
}
fn main(){
让mut-tricky=这是什么{
名称:“Annabelle”。to_string(),
绰号:无,
};
tricky.昵称=一些(&tricky.name[…4]);
println!(“{:?}”,狡猾);
}
从某种意义上说,这确实有效,但创造的价值受到高度限制——它永远无法移动。值得注意的是,这意味着它不能从函数返回,也不能通过值传递给任何对象。构造函数显示了与上述寿命相同的问题:

fn创建者{/*…*/}
如果你试图用一种方法来编写同样的代码,你将需要一种诱人但最终无用的
&'a self
。当涉及到这一点时,此代码甚至会受到更大的限制,并且在第一次方法调用后,您将出现借用检查器错误:

#[派生(调试)]
结构是什么,
}
恳求{
fn打个结(&'a mut self){
self.昵称=一些(&self.name[…4]);
}
}
fn main(){
让mut-tricky=这是什么{
名称:“Annabelle”。to_string(),
绰号:无,
};
棘手的。打个结();
//不能将'tricky'作为不可变项借用,因为它也是作为可变项借用的
//println!(“{:?}”,狡猾);
}
另见:

关于
Pin
呢? ,在锈1.33中稳定,具有以下特性:

这种情况的一个主要例子是构建自引用结构,因为将带有指向自身的指针的对象移动将使其无效,这可能导致未定义的行为

需要注意的是,“自我参照”并不一定意味着使用参照。事实上,报告明确指出(我的重点):

我们不能用普通的引用通知编译器, 因为这种模式不能用通常的借用规则来描述。 相反,我们使用原始指针,尽管已知该指针不为空, 因为我们知道它是
impl Parent {
    fn child<'a>(&'a self) -> Child<'a> { /* ... */ }
}
fn make_combined<'a>() -> Combined<'a> { /* ... */ }
impl<'a> Combined<'a> {
    fn new() -> Combined<'a> { /* ... */ }
}
let a = Object::new();
let b = a;
let c = b;
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
    }
}