首页 \ 问答 \ 0x来自哪里?(where does 0x come from? [duplicate])

0x来自哪里?(where does 0x come from? [duplicate])

可能重复:
为什么十六进制的前缀为0x?

我刚刚看到我的一个朋友发表的评论:

3x12=36
2x12=24
1x12=12
0x12=18

这让我很想知道..
为什么他们选择0x作为十六进制数字的前缀? 这个决定背后有什么历史吗?


Possible Duplicate:
Why are Hexadecimal Prefixed as 0x?

I just saw a comment a friend of mine made:

3x12=36
2x12=24
1x12=12
0x12=18

Which made me wonder..
Why did they choose 0x as prefix for hexadecimal numbers? Is there any history behind this decision?


原文:https://stackoverflow.com/questions/4257661
更新时间:2023-05-03 17:05

最满意答案

我们来看一下这个简单的实现 :

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 p = Parent { count: 42 };
        let c = Child { parent: &p };

        Combined { parent: p, child: c }
    }
}

fn main() {}

这将失败,稍微清理错误:

error: `p` does not live long enough
  --> src/main.rs:17:34
   |
17 |         let c = Child { parent: &p };
   |                                  ^
   |
note: reference must be valid for the lifetime 'a as defined
      on the block at 15:21...
  --> src/main.rs:15:22
   |
15 |     fn new() -> Self {
   |                      ^
note: ...but borrowed value is only valid for the block suffix
      following statement 0 at 16:37
  --> src/main.rs:16:38
   |
16 |         let p = Parent { count: 42 };
   |                                      ^

要完全理解这个错误,你必须考虑如何在内存中表示值,以及在移动这些值时会发生什么。 我们用一些假设的内存地址来注释Combined::new ,它们显示了值的位置:

let p = Parent { count: 42 };
// `p` lives at address 0x1000 and takes up 4 bytes
// The value of `p` is 42 
let c = Child { parent: &p };
// `c` lives at address 0x1010 and takes up 4 bytes
// The value of `c` is 0x1000

Combined { parent: p, child: c }
// The return value lives at address 0x2000 and takes up 8 bytes
// `p` is moved to 0x2000
// `c` is ... ?

c应该怎么办? 如果值像p一样移动,那么它将引用不再保证在其中具有有效值的内存。 任何其他代码片段都允许在内存地址0x1000存储值。 假设它是一个整数访问该内存可能会导致崩溃和/或安全错误,并且是Rust防止的主要错误类别之一。

这正是生命预防的问题。 生命周期是一些元数据,允许您和编译器知道值在其当前内存位置有效多长时间。 这是一个重要的区别,因为这是Rust新手做的一个常见的错误。 生锈时间不是创建对象和被破坏之间的时间段!

作为一个比喻,以这种方式思考:在一个人的生活中,他们将居住在许多不同的地方,每个地点都有不同的地址。 生锈的生活关心你现在所在的地址,而不是每当你将来会死亡(尽管死亡也改变你的地址)。 每次你移动它是相关的,因为你的地址不再有效。

同样重要的是要注意,生命周期不会改变你的代码; 您的代码控制生命周期,您的生命周期不能控制代码。 幽默的说法是“生命是描述性的,而不是规范性的”。

我们用一些行号注释Combined::new ,我们将用它来突出显示生命周期:

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

p具体寿命为1到4,其中我将代表[1,4] )。 c的具体寿命为[2,4] ,返回值的具体寿命为[4,5] 。 有可能具有从零开始的具体生命周期 - 这将表示函数的一个参数的生命周期或者在块之外存在的东西。

注意, c本身的寿命是[2,4] ,但它指的是一个寿命为[1,4] 。 只要引用值在引用值之前变得无效,这是很好的。 当我们尝试从块中返回c时,会出现此问题。 这将超出其自然长度“超越”一生。

这个新知识应该说明前两个例子。 第三个需要查看Parent::child的实现。 有机会,它看起来像这样:

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

这将使用终身检测来避免编写显式的通用生命周期参数 。 相当于:

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

在这两种情况下,该方法表示将返回一个已被参数化的Child结构,并具有具体的self生命周期。 换句话说, Child实例包含一个引用,它创建它的Parent ,因此不能比Parent实例活得更长。

这也让我们认识到我们的创作功能真的是错误的:

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

虽然你更有可能看到这个写成不同的形式:

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

在这两种情况下,都没有通过参数提供生命周期参数。 这意味着Combined将被参数化的生命周期不受任何限制 - 它可以是任何呼叫者想要的。 这是无意义的,因为调用者可以指定'static生命周期,没有办法满足该条件。

如何解决?

最简单和最推荐的解决方案是不要将这些项目放在同一个结构中。 通过这样做,您的结构嵌套将模仿您的代码的生命周期。 将数据类型放在一起的结构中,然后提供允许您根据需要获取包含引用的引用或对象的方法。

有一个特殊情况下,生命周期跟踪是过度的:当你有堆放在堆上的东西。 例如,当您使用Box<T>时会发生这种情况。 在这种情况下,被移动的结构包含指向堆的指针。 指向值将保持稳定,但指针本身的地址将移动。 实际上,这并不重要,因为你总是遵循指针。

租赁箱子或owning_ref箱子是表示这种情况的方法,但是它们要求基地址不会移动 。 这排除了突变向量,这可能导致重新分配和堆分配值的移动。

更多信息

p移入结构体后,为什么编译器无法获取对p的新引用,并将其分配给struct中的c

在理论上可以做到这一点,这样做会引起大量的复杂性和开销。 每次对象被移动时,编译器都需要插入代码来“修复”引用。 这意味着复制结构不再是一个非常便宜的操作,只是移动一些位。 这甚至可能意味着这样的代码是昂贵的,这取决于假设的优化器是多么好:

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

而不是强制这种情况发生在每一个动作中,程序员可以选择何时发生这种情况,通过创建仅在您调用它们时才会使用相应引用的方法。


有一个具体的例子,你可以创建一个引用自己的类型。 您需要使用像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);
}

这在某种意义上是有效的,但是创造的价值是非常有限的 - 它永远不会被移动。 值得注意的是,这意味着它不能从函数返回或者通过值被传递给任何东西。 构造函数显示与上述相同的生命周期问题:

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

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:

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? 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. Any other piece of code is allowed to store values at memory address 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.

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 lifetimes are not the time period between when an object is created and when it is destroyed!

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). 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:

{                                          // 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]). The concrete lifetime of child is [2,4], and the concrete lifetime of the return value is [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]. 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. 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. 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. Said another way, the Child instance contains a reference to the Parent that created it, and thus cannot live longer than that Parent instance.

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. This is nonsensical, because the caller could specify the 'static lifetime and there's no way to meet that condition.

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. 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.

The rental crate or the owning_ref crate are ways of representing this case, but they require that the base address never move. 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.

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?

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:

#[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> { /* ... */ }

What about Pin?

Pin, stabilized in Rust 1.33, has this in the module documentation:

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. Indeed, owning-ref and rental use raw pointers under the hood.

The only thing that Pin adds to the table is a common way to state that a given value is guaranteed to not move.

See also:

相关问答

更多

相关文章

更多

最新问答

更多
  • 您如何使用git diff文件,并将其应用于同一存储库的副本的本地分支?(How do you take a git diff file, and apply it to a local branch that is a copy of the same repository?)
  • 将长浮点值剪切为2个小数点并复制到字符数组(Cut Long Float Value to 2 decimal points and copy to Character Array)
  • OctoberCMS侧边栏不呈现(OctoberCMS Sidebar not rendering)
  • 页面加载后对象是否有资格进行垃圾回收?(Are objects eligible for garbage collection after the page loads?)
  • codeigniter中的语言不能按预期工作(language in codeigniter doesn' t work as expected)
  • 在计算机拍照在哪里进入
  • 使用cin.get()从c ++中的输入流中丢弃不需要的字符(Using cin.get() to discard unwanted characters from the input stream in c++)
  • No for循环将在for循环中运行。(No for loop will run inside for loop. Testing for primes)
  • 单页应用程序:页面重新加载(Single Page Application: page reload)
  • 在循环中选择具有相似模式的列名称(Selecting Column Name With Similar Pattern in a Loop)
  • System.StackOverflow错误(System.StackOverflow error)
  • KnockoutJS未在嵌套模板上应用beforeRemove和afterAdd(KnockoutJS not applying beforeRemove and afterAdd on nested templates)
  • 散列包括方法和/或嵌套属性(Hash include methods and/or nested attributes)
  • android - 如何避免使用Samsung RFS文件系统延迟/冻结?(android - how to avoid lag/freezes with Samsung RFS filesystem?)
  • TensorFlow:基于索引列表创建新张量(TensorFlow: Create a new tensor based on list of indices)
  • 企业安全培训的各项内容
  • 错误:RPC失败;(error: RPC failed; curl transfer closed with outstanding read data remaining)
  • C#类名中允许哪些字符?(What characters are allowed in C# class name?)
  • NumPy:将int64值存储在np.array中并使用dtype float64并将其转换回整数是否安全?(NumPy: Is it safe to store an int64 value in an np.array with dtype float64 and later convert it back to integer?)
  • 注销后如何隐藏导航portlet?(How to hide navigation portlet after logout?)
  • 将多个行和可变行移动到列(moving multiple and variable rows to columns)
  • 提交表单时忽略基础href,而不使用Javascript(ignore base href when submitting form, without using Javascript)
  • 对setOnInfoWindowClickListener的意图(Intent on setOnInfoWindowClickListener)
  • Angular $资源不会改变方法(Angular $resource doesn't change method)
  • 在Angular 5中不是一个函数(is not a function in Angular 5)
  • 如何配置Composite C1以将.m和桌面作为同一站点提供服务(How to configure Composite C1 to serve .m and desktop as the same site)
  • 不适用:悬停在悬停时:在元素之前[复制](Don't apply :hover when hovering on :before element [duplicate])
  • 常见的python rpc和cli接口(Common python rpc and cli interface)
  • Mysql DB单个字段匹配多个其他字段(Mysql DB single field matching to multiple other fields)
  • 产品页面上的Magento Up出售对齐问题(Magento Up sell alignment issue on the products page)