首页 \ 问答 \ border-radius + background-color ==裁剪边框(border-radius + background-color == cropped border)

border-radius + background-color ==裁剪边框(border-radius + background-color == cropped border)

考虑应用border-radiusborderbackground-color CSS属性的div

<div style="background-color:#EEEEEE; border-radius:10px; border: 1px black solid;">
  Blah
</div>

在此输入图像说明

现在考虑一个类似的布局,但是在内部div中指定了background-color

<div style="border-radius:10px; border: 1px black solid;">
  <div style="background-color:#EEEEEE;">
    Blah
  </div>
</div>

在此输入图像说明

令我感到沮丧的是, 内部 <div>background-color掩盖了外部 <div>的边框。

这是问题的简化示例。 实际上,我正在使用<table>作为具有交替行颜色的内部元素。 而且我使用<div>作为外部元素,因为border-radius<table>元素上似乎不起作用。 这是一个这个问题的例子的jsfiddle 。

有没有人有解决方案的建议?


Consider a div with the border-radius, border, and background-color CSS attributes applied:

<div style="background-color:#EEEEEE; border-radius:10px; border: 1px black solid;">
  Blah
</div>

enter image description here

Now consider a similar layout but with the background-color specified in an inner-div:

<div style="border-radius:10px; border: 1px black solid;">
  <div style="background-color:#EEEEEE;">
    Blah
  </div>
</div>

enter image description here

I'm dismayed by the fact that the background-color of the inner <div> is obscuring the border of the outer <div>.

This is a simplified sample of the problem. In reality, I'm using a <table> as the inner element with alternating row colors. And I'm using a <div> as the outer element since border-radius does not seem to work on the <table> element. Here's a jsfiddle of a sample of this problem.

Does anyone have a suggestion for a solution?


原文:https://stackoverflow.com/questions/6312067
更新时间:2023-05-30 22:05

最满意答案

警告:我是从我没有的专业知识水平说话的。 鉴于这篇文章的长度,我可能错误了很多次。

TL; DR:顶级价值的生存期是逆变的。 参考值的寿命是不变的。

介绍问题

你可以通过将VecRef<'a>替换&'a mut T VecRef<'a>显着简化你的例子。

此外,应该删除main ,因为与某些特定的生命实例相比,讨论函数的一般行为更完整。

而不是VecRefRef的构造函数,让我们使用这个函数:

fn use_same_ref_ref<'c>(reference: &'c mut &'c mut ()) {}

在我们进一步研究之前,了解生命如何在Rust中隐式生成是很重要的。 当一个指针指向另一个显式注释的名称时,会发生一次生命强制。 这允许的最明显的事情是缩短顶级指针的生命周期。 因此,这不是一个典型的举措。

除此之外:我说“明确注释”,因为在像let x = yfn f<T>(_: T) {}这样的隐式情况下,再借贷似乎不会发生 。 目前还不清楚这是否是有意的。

那么完整的例子就是

fn use_same_ref_ref<'c>(reference: &'c mut &'c mut ()) {}

fn use_ref_ref<'a, 'b>(reference: &'a mut &'b mut ()) {
    use_same_ref_ref(reference);
}

这给出了同样的错误:

<anon>:6:22: 6:31 error: cannot infer an appropriate lifetime for automatic coercion due to conflicting requirements
<anon>:6     use_same_ref_ref(reference);
                              ^~~~~~~~~

一个微不足道的修复

显然,人们可以通过这样做来解决它

fn use_same_ref_ref<'c>(reference: &'c mut &'c mut ()) {}

fn use_ref_ref<'a>(reference: &'a mut &'a mut ()) {
    use_same_ref_ref(reference);
}

因为签名现在在逻辑上是相同的。 但是,不明显的是为什么

let mut val = ();
let mut reference = &mut val;
let ref_ref = &mut reference;

use_ref_ref(ref_ref);

能够产生&'a mut &'a mut ()

一个不太重要的修复

可以改为执行'a: 'b

fn use_same_ref_ref<'c>(reference: &'c mut &'c mut ()) {}

fn use_ref_ref<'a: 'b, 'b>(reference: &'a mut &'b mut ()) {
    use_same_ref_ref(reference);
}

这意味着外部参照的寿命至少与内部参照的寿命一样大。

这并不明显

  • 为什么&'a mut &'b mut ()不能转换为&'c mut &'c mut () ,或者

  • 这是否比&'a mut &'a mut ()更好。

我希望回答这些问题。

一个不确定的

声明'b: 'a不能解决问题。

fn use_same_ref_ref<'c>(reference: &'c mut &'c mut ()) {}

fn use_ref_ref<'a, 'b: 'a>(reference: &'a mut &'b mut ()) {
    use_same_ref_ref(reference);
}

另一个更令人惊讶的修复

使外部引用immutable可以解决问题

fn use_same_ref_ref<'c>(reference: &'c &'c mut ()) {}

fn use_ref_ref<'a, 'b>(reference: &'a &'b mut ()) {
    use_same_ref_ref(reference);
}

而更令人惊讶的不修复!

使内部参考不可变根本没有帮助!

fn use_same_ref_ref<'c>(reference: &'c mut &'c ()) {}

fn use_ref_ref<'a, 'b>(reference: &'a mut &'b ()) {
    use_same_ref_ref(reference);
}

但为什么??!

原因是...

坚持下去,首先我们讨论差异

计算机科学中两个非常重要的概念是协变逆变 。 我不打算使用这些名称(我会非常明确地指出我要投哪些东西),但这些名称对于搜索互联网仍然非常有用。

理解方差的概念非常重要,然后才能理解这里的行为。 如果您已经参加了涵盖此课程的大学课程,或者您可以从其他方面记住它,那么您处于良好的位置。 不过,您仍然可以欣赏将这个想法与生活时间联系起来的帮助。

简单的情况 - 一个正常的指针

用一个指针考虑一些堆栈位置:

    ║ Name      │ Type                │ Value
 ───╫───────────┼─────────────────────┼───────
  1 ║ val       │ i32                 │ -1
 ───╫───────────┼─────────────────────┼───────
  2 ║ reference │ &'x mut i32         │ 0x1

堆栈向下生长,所以reference堆栈位置是在val之后创建的,并且会在val之前被移除。

考虑你的确如此

let new_ref = reference;

要得到

    ║ Name      │ Type        │ Value  
 ───╫───────────┼─────────────┼─────── 
  1 ║ val       │ i32         │ -1     
 ───╫───────────┼─────────────┼─────── 
  2 ║ reference │ &'x mut i32 │ 0x1    
 ───╫───────────┼─────────────┼─────── 
  3 ║ new_ref   │ &'y mut i32 │ 0x1    

什么样的生命对'y有效?

考虑两个可变指针操作:

Read阻止'y增长,因为'x引用只能保证对象在'x范围内保持活动状态。 但是, read并不能防止'y缩小,因为当指向的值处于活动状态时,任何读取都会导致独立于生命周期'y

写入防止'y也增长,因为不能写入无效的指针。 但是, 写入不会阻止'y的缩小,因为对指针的任何写入都会将该值复制到其中,从而使得它的生命周期'y不变。

困难的情况 - 一个指针指针

考虑一下带指针指针的堆栈位置:

    ║ Name      │ Type                │ Value  
 ───╫───────────┼─────────────────────┼─────── 
  1 ║ val       │ i32                 │ -1     
 ───╫───────────┼─────────────────────┼─────── 
  2 ║ reference │ &'a mut i32         │ 0x1    
 ───╫───────────┼─────────────────────┼─────── 
  3 ║ ref_ref   │ &'x mut &'a mut i32 │ 0x2    

考虑你的确如此

let new_ref_ref = ref_ref;

要得到

    ║ Name        │ Type                │ Value  
 ───╫─────────────┼─────────────────────┼─────── 
  1 ║ val         │ i32                 │ -1     
 ───╫─────────────┼─────────────────────┼─────── 
  2 ║ reference   │ &'a mut i32         │ 0x1    
 ───╫─────────────┼─────────────────────┼─────── 
  3 ║ ref_ref     │ &'x mut &'a mut i32 │ 0x2    
 ───╫─────────────┼─────────────────────┼─────── 
  4 ║ new_ref_ref │ &'y mut &'b mut i32 │ 0x2    

现在有两个问题:

  1. 什么样的生命对'y有效?

  2. 什么样的生命对'b有效?

让我们首先考虑y与两个可变指针操作:

Read阻止'y增长,因为'x引用只能保证对象在'x范围内保持活动状态。 但是, read并不能防止'y缩小,因为当指向的值处于活动状态时,任何读取都将导致独立于生存期'y

写入防止'y也增长,因为不能写入无效的指针。 但是, 写入并不能防止'y缩小,因为对指针的任何写入都会将值复制到其中,从而使得它的生命周期'y不变。

这和以前一样。

现在,考虑两个可变指针操作

Read阻止'b增长,因为如果要从外部指针中提取内部指针,您可以在'a过期后读取它。

防止'b也增长,因为如果要从外部指针中提取内部指针,您可以在'a过期后写入它。

由于这种情况,一起读写可以防止'b缩小,

let ref_ref: &'x mut &'a mut i32 = ...;

{
    // Has lifetime 'b, which is smaller than 'a
    let new_val: i32 = 123;

    // Shrink 'a to 'b
    let new_ref_ref: &'x mut &'b mut i32 = ref_ref;

    *new_ref_ref = &mut new_val;
}

// new_ref_ref is out of scope, so ref_ref is usable again
let ref_ref: &'a mut i32 = *ref_ref;
// Oops, we have an &'a mut i32 pointer to a dropped value!

Ergo, 'b不能缩小,它不能从'a ,因此'a == 'b准确地增长。

好的,这是否解决了我们的问题?

记住代码?

fn use_same_ref_ref<'c>(reference: &'c mut &'c mut ()) {}

fn use_ref_ref<'a, 'b>(reference: &'a mut &'b mut ()) {
    use_same_ref_ref(reference);
}

当您调用use_same_ref_ref ,会尝试进行use_same_ref_ref

&'a mut &'b mut ()  →  &'c mut &'c mut ()

现在注意'b == 'c因为我们讨论了方差。 因此我们实际上是在铸造

&'a mut &'b mut ()  →  &'b mut &'b mut ()

外面的&'a只能收缩。 为了做到这一点,编译器需要知道

'a: 'b

编译器不知道这一点,所以编译失败。

我们其他的例子呢?

第一个是

fn use_same_ref_ref<'c>(reference: &'c mut &'c mut ()) {}

fn use_ref_ref<'a>(reference: &'a mut &'a mut ()) {
    use_same_ref_ref(reference);
}

而不是'a: 'b ,编译器现在需要'a: 'a ,这是非常正确的。

第二个直接主张'a: 'b

fn use_same_ref_ref<'c>(reference: &'c mut &'c mut ()) {}

fn use_ref_ref<'a: 'b, 'b>(reference: &'a mut &'b mut ()) {
    use_same_ref_ref(reference);
}

第三个断言是'b: 'a

fn use_same_ref_ref<'c>(reference: &'c mut &'c mut ()) {}

fn use_ref_ref<'a, 'b: 'a>(reference: &'a mut &'b mut ()) {
    use_same_ref_ref(reference);
}

这不起作用,因为这不是所需的断言。

关于不变性呢?

我们在这里有两个案例。 首先是使外部引用不可变。

fn use_same_ref_ref<'c>(reference: &'c &'c mut ()) {}

fn use_ref_ref<'a, 'b>(reference: &'a &'b mut ()) {
    use_same_ref_ref(reference);
}

这一个工作。 为什么?

那么,考虑我们之前收缩的问题:

由于这种情况,一起读写可以防止'b缩小,

let ref_ref: &'x mut &'a mut i32 = ...;

{
    // Has lifetime 'b, which is smaller than 'a
    let new_val: i32 = 123;

    // Shrink 'a to 'b
    let new_ref_ref: &'x mut &'b mut i32 = ref_ref;

    *new_ref_ref = &mut new_val;
}

// new_ref_ref is out of scope, so ref_ref is usable again
let ref_ref: &'a mut i32 = *ref_ref;
// Oops, we have an &'a mut i32 pointer to a dropped value!

Ergo, 'b不能缩小,它不能从'a ,因此'a == 'b准确地增长。

这只能发生,因为我们可以将内部参考换成一些新的,不够长寿命的参考。 如果我们无法交换参考,这不是问题。 因此缩小内部参考的寿命是可能的。

而失败的呢?

使内部引用不可变无助于:

fn use_same_ref_ref<'c>(reference: &'c mut &'c ()) {}

fn use_ref_ref<'a, 'b>(reference: &'a mut &'b ()) {
    use_same_ref_ref(reference);
}

当你认为之前提到的问题从不涉及从内部引用的任何读取时,这是有意义的。 事实上,这里修改了有问题的代码来证明:

let ref_ref: &'x mut &'a i32 = ...;

{
    // Has lifetime 'b, which is smaller than 'a
    let new_val: i32 = 123;

    // Shrink 'a to 'b
    let new_ref_ref: &'x mut &'b i32 = ref_ref;

    *new_ref_ref = &new_val;
}

// new_ref_ref is out of scope, so ref_ref is usable again
let ref_ref: &'a i32 = *ref_ref;
// Oops, we have an &'a i32 pointer to a dropped value!

还有一个问题

这已经很长了,但回想一下:

可以改为执行'a: 'b

fn use_same_ref_ref<'c>(reference: &'c mut &'c mut ()) {}

fn use_ref_ref<'a: 'b, 'b>(reference: &'a mut &'b mut ()) {
    use_same_ref_ref(reference);
}

这意味着外部参照的寿命至少与内部参照的寿命一样大。

这并不明显

  • 为什么&'a mut &'b mut ()不能转换为&'c mut &'c mut () ,或者

  • 这是否比&'a mut &'a mut ()更好。

我希望回答这些问题。

我们已经回答了第一个问题,但第二个问题呢? 'a: 'b允许超过'a == 'b吗?

考虑一些类型为&'x mut &'y mut ()调用者。 如果'x : 'y ,那么它会自动转换为&'y mut &'y mut () 。 相反,如果'x == 'y ,那么'x : 'y已经存在! 因此,只有当您希望将包含'x的类型返回给调用者时,差异才是重要的,调用者是唯一能够区分这两者的人。 由于这不是这种情况,所以两者是等价的。

还有一件事

如果你写

let mut val = ();
let mut reference = &mut val;
let ref_ref = &mut reference;

use_ref_ref(ref_ref);

其中定义了use_ref_ref

fn use_ref_ref<'a: 'b, 'b>(reference: &'a mut &'b mut ()) {
    use_same_ref_ref(reference);
}

代码如何能够执行'a: 'b ? 它看起来像是相反的检验!

那么记住这一点

let reference = &mut val;

能够缩短其使用寿命,因为这是此时的外部使用寿命。 因此,即使当指针在该生命周期之外时,它也可以引用比val的实际生命周期更小的生命周期!


Warning: I'm speaking from a level of expertise that I don't really have. Given the length of this post, I'm probably wrong a large number of times.

TL;DR: Lifetimes of top-level values are contravariant. Lifetimes of referenced values are invariant.

Introducing the problem

You can simplify your example significantly, by replacing VecRef<'a> with &'a mut T.

Further, one should remove main, since it's more complete to talk about the general behaviour of a function than some particular lifetime instantiation.

Instead of VecRefRef's constructor, let's use this function:

fn use_same_ref_ref<'c>(reference: &'c mut &'c mut ()) {}

Before we go further, it's important to understand how lifetimes get implicitly cast in Rust. When one assigns a pointer to another explicitly annotated name, lifetime coercion happens. The most obvious thing this allows is shrinking the lifetime of the top-level pointer. As such, this is not a typical move.

Aside: I say "explicitly annotated" because in implicit cases like let x = y or fn f<T>(_: T) {}, reborrowing doesn't seem to happen. It is not clear whether this is intended.

The full example is then

fn use_same_ref_ref<'c>(reference: &'c mut &'c mut ()) {}

fn use_ref_ref<'a, 'b>(reference: &'a mut &'b mut ()) {
    use_same_ref_ref(reference);
}

which gives the same error:

error[E0623]: lifetime mismatch
 --> src/main.rs:5:26
  |
4 |     fn use_ref_ref<'a, 'b>(reference: &'a mut &'b mut ()) {
  |                                       ------------------
  |                                       |
  |                                       these two types are declared with different lifetimes...
5 |         use_same_ref_ref(reference);
  |                          ^^^^^^^^^ ...but data from `reference` flows into `reference` here

A trivial fix

One can fix it by doing

fn use_same_ref_ref<'c>(reference: &'c mut &'c mut ()) {}

fn use_ref_ref<'a>(reference: &'a mut &'a mut ()) {
    use_same_ref_ref(reference);
}

since the signatures are now logically the same. However, what is not obvious is why

let mut val = ();
let mut reference = &mut val;
let ref_ref = &mut reference;

use_ref_ref(ref_ref);

is able to produce an &'a mut &'a mut ().

A less trivial fix

One can instead enforce 'a: 'b

fn use_same_ref_ref<'c>(reference: &'c mut &'c mut ()) {}

fn use_ref_ref<'a: 'b, 'b>(reference: &'a mut &'b mut ()) {
    use_same_ref_ref(reference);
}

This means that the lifetime of the outer reference is at least as large as the lifetime of the inner one.

It's not obvious

  • why &'a mut &'b mut () is not castable to &'c mut &'c mut (), or

  • whether this is better than &'a mut &'a mut ().

I hope to answer these questions.

A non-fix

Asserting 'b: 'a does not fix the problem.

fn use_same_ref_ref<'c>(reference: &'c mut &'c mut ()) {}

fn use_ref_ref<'a, 'b: 'a>(reference: &'a mut &'b mut ()) {
    use_same_ref_ref(reference);
}

Another, more surprising fix

Making the outer reference immutable fixes the problem

fn use_same_ref_ref<'c>(reference: &'c &'c mut ()) {}

fn use_ref_ref<'a, 'b>(reference: &'a &'b mut ()) {
    use_same_ref_ref(reference);
}

And an even more surprising non-fix!

Making the inner reference immutable doesn't help at all!

fn use_same_ref_ref<'c>(reference: &'c mut &'c ()) {}

fn use_ref_ref<'a, 'b>(reference: &'a mut &'b ()) {
    use_same_ref_ref(reference);
}

BUT WHY??!

And the reason is...

Hold on, first we cover variance

Two very important concepts in computer science are covariance and contravariance. I'm not going to use these names (I'll be very explicit about which way I'm casting things) but those names are still very useful for searching the internet.

It's very important to understand the concept of variance before you can understand the behaviour here. If you've taken a university course that covers this, or you can remember it from some other context, you're in a good position. You might still appreciate the help linking the idea to lifetimes, though.

The simple case - a normal pointer

Consider some stack positions with a pointer:

    ║ Name      │ Type                │ Value
 ───╫───────────┼─────────────────────┼───────
  1 ║ val       │ i32                 │ -1
 ───╫───────────┼─────────────────────┼───────
  2 ║ reference │ &'x mut i32         │ 0x1

The stack grows downwards, so the reference stack position was created after val, and will be removed before val is.

Consider that you do

let new_ref = reference;

to get

    ║ Name      │ Type        │ Value  
 ───╫───────────┼─────────────┼─────── 
  1 ║ val       │ i32         │ -1     
 ───╫───────────┼─────────────┼─────── 
  2 ║ reference │ &'x mut i32 │ 0x1    
 ───╫───────────┼─────────────┼─────── 
  3 ║ new_ref   │ &'y mut i32 │ 0x1    

What lifetimes are valid for 'y?

Consider the two mutable pointer operations:

  • Read
  • Write

Read prevents 'y from growing, because a 'x reference only guarantees the object stays alive during the scope of 'x. However, read does not prevent 'y from shrinking since any read when the pointed-to value is alive will result in a value independent of the lifetime 'y.

Write prevents 'y from growing also, since one cannot write to an invalidated pointer. However, write does not prevent 'y from shrinking since any write to the pointer copies the value in, which leaves it invariant of the lifetime 'y.

The hard case - a pointer pointer

Consider some stack positions with a pointer pointer:

    ║ Name      │ Type                │ Value  
 ───╫───────────┼─────────────────────┼─────── 
  1 ║ val       │ i32                 │ -1     
 ───╫───────────┼─────────────────────┼─────── 
  2 ║ reference │ &'a mut i32         │ 0x1    
 ───╫───────────┼─────────────────────┼─────── 
  3 ║ ref_ref   │ &'x mut &'a mut i32 │ 0x2    

Consider that you do

let new_ref_ref = ref_ref;

to get

    ║ Name        │ Type                │ Value  
 ───╫─────────────┼─────────────────────┼─────── 
  1 ║ val         │ i32                 │ -1     
 ───╫─────────────┼─────────────────────┼─────── 
  2 ║ reference   │ &'a mut i32         │ 0x1    
 ───╫─────────────┼─────────────────────┼─────── 
  3 ║ ref_ref     │ &'x mut &'a mut i32 │ 0x2    
 ───╫─────────────┼─────────────────────┼─────── 
  4 ║ new_ref_ref │ &'y mut &'b mut i32 │ 0x2    

Now there are two questions:

  1. What lifetimes are valid for 'y?

  2. What lifetimes are valid for 'b?

Let's first consider y with the two mutable pointer operations:

  • Read
  • Write

Read prevents 'y from growing, because a 'x reference only guarantees the object stays alive during the scope of 'x. However, read does not prevent 'y from shrinking since any read when the pointed-to value is alive will result in a value independent of the lifetime 'y.

Write prevents 'y from growing also, since one cannot write to an invalidated pointer. However, write does not prevent 'y from shrinking since any write to the pointer copies the value in, which leaves it invariant of the lifetime 'y.

This is the same as before.

Now, consider 'b with the two mutable pointer operations

Read prevents 'b from growing, since if one was to extract the inner pointer from the outer pointer you would be able to read it after 'a has expired.

Write prevents 'b from growing also, since if one was to extract the inner pointer from the outer pointer you would be able to write to it after 'a has expired.

Read and write together also prevent 'b from shrinking, because of this scenario:

let ref_ref: &'x mut &'a mut i32 = ...;

{
    // Has lifetime 'b, which is smaller than 'a
    let new_val: i32 = 123;

    // Shrink 'a to 'b
    let new_ref_ref: &'x mut &'b mut i32 = ref_ref;

    *new_ref_ref = &mut new_val;
}

// new_ref_ref is out of scope, so ref_ref is usable again
let ref_ref: &'a mut i32 = *ref_ref;
// Oops, we have an &'a mut i32 pointer to a dropped value!

Ergo, 'b cannot shrink and it cannot grow from 'a, so 'a == 'b exactly.

OK, does this solve our questions?

Remember the code?

fn use_same_ref_ref<'c>(reference: &'c mut &'c mut ()) {}

fn use_ref_ref<'a, 'b>(reference: &'a mut &'b mut ()) {
    use_same_ref_ref(reference);
}

When you call use_same_ref_ref, a cast is attempted

&'a mut &'b mut ()  →  &'c mut &'c mut ()

Now note that 'b == 'c because of our discussion about variance. Thus we are actually casting

&'a mut &'b mut ()  →  &'b mut &'b mut ()

The outer &'a can only be shrunk. In order to do this, the compiler needs to know

'a: 'b

The compiler does not know this, and so fails compilation.

What about our other examples?

The first was

fn use_same_ref_ref<'c>(reference: &'c mut &'c mut ()) {}

fn use_ref_ref<'a>(reference: &'a mut &'a mut ()) {
    use_same_ref_ref(reference);
}

Instead of 'a: 'b, the compiler now needs 'a: 'a, which is trivially true.

The second directly asserted 'a: 'b

fn use_same_ref_ref<'c>(reference: &'c mut &'c mut ()) {}

fn use_ref_ref<'a: 'b, 'b>(reference: &'a mut &'b mut ()) {
    use_same_ref_ref(reference);
}

The third asserted 'b: 'a

fn use_same_ref_ref<'c>(reference: &'c mut &'c mut ()) {}

fn use_ref_ref<'a, 'b: 'a>(reference: &'a mut &'b mut ()) {
    use_same_ref_ref(reference);
}

This does not work, because this is not the needed assertion.

What about immutability?

We had two cases here. The first was to make the outer reference immutable.

fn use_same_ref_ref<'c>(reference: &'c &'c mut ()) {}

fn use_ref_ref<'a, 'b>(reference: &'a &'b mut ()) {
    use_same_ref_ref(reference);
}

This one worked. Why?

Well, consider our problem with shrinking &'b from before:

Read and write together also prevent 'b from shrinking, because of this scenario:

let ref_ref: &'x mut &'a mut i32 = ...;

{
    // Has lifetime 'b, which is smaller than 'a
    let new_val: i32 = 123;

    // Shrink 'a to 'b
    let new_ref_ref: &'x mut &'b mut i32 = ref_ref;

    *new_ref_ref = &mut new_val;
}

// new_ref_ref is out of scope, so ref_ref is usable again
let ref_ref: &'a mut i32 = *ref_ref;
// Oops, we have an &'a mut i32 pointer to a dropped value!

Ergo, 'b cannot shrink and it cannot grow from 'a, so 'a == 'b exactly.

This can only happen because we can swap the inner reference for some new, insufficiently long lived reference. If we are not able to swap the reference, this is not a problem. Thus shrinking the lifetime of the inner reference is possible.

And the failing one?

Making the inner reference immutable does not help:

fn use_same_ref_ref<'c>(reference: &'c mut &'c ()) {}

fn use_ref_ref<'a, 'b>(reference: &'a mut &'b ()) {
    use_same_ref_ref(reference);
}

This makes sense when you consider that the problem mentioned before never involves any reads from the inner reference. In fact, here's the problematic code modified to demonstrate that:

let ref_ref: &'x mut &'a i32 = ...;

{
    // Has lifetime 'b, which is smaller than 'a
    let new_val: i32 = 123;

    // Shrink 'a to 'b
    let new_ref_ref: &'x mut &'b i32 = ref_ref;

    *new_ref_ref = &new_val;
}

// new_ref_ref is out of scope, so ref_ref is usable again
let ref_ref: &'a i32 = *ref_ref;
// Oops, we have an &'a i32 pointer to a dropped value!

There was another question

It's been quite long, but think back to:

One can instead enforce 'a: 'b

fn use_same_ref_ref<'c>(reference: &'c mut &'c mut ()) {}

fn use_ref_ref<'a: 'b, 'b>(reference: &'a mut &'b mut ()) {
    use_same_ref_ref(reference);
}

This means that the lifetime of the outer reference is at least as large as the lifetime of the inner one.

It's not obvious

  • why &'a mut &'b mut () is not castable to &'c mut &'c mut (), or

  • whether this is better than &'a mut &'a mut ().

I hope to answer these questions.

We've answered the first bullet-pointed question, but what about the second? Does 'a: 'b permit more than 'a == 'b?

Consider some caller with type &'x mut &'y mut (). If 'x : 'y, then it will be automatically cast to &'y mut &'y mut (). Instead, if 'x == 'y, then 'x : 'y holds already! The difference is thus only important if you wish to return a type containing 'x to the caller, who is the only one able to distinguish the two. Since this is not the case here, the two are equivalent.

One more thing

If you write

let mut val = ();
let mut reference = &mut val;
let ref_ref = &mut reference;

use_ref_ref(ref_ref);

where use_ref_ref is defined

fn use_ref_ref<'a: 'b, 'b>(reference: &'a mut &'b mut ()) {
    use_same_ref_ref(reference);
}

how is the code able to enforce 'a: 'b? It looks on inspection like the opposite is true!

Well, remember that

let reference = &mut val;

is able to shrink its lifetime, since it's the outer lifetime at this point. Thus, it can refer to a lifetime smaller than the real lifetime of val, even when the pointer is outside of that lifetime!

相关问答

更多
  • 你确定你的版本是一样的吗? 你能用git回到那个版本吗? 所以你的项目是找到至少2份ivar的DocumentViewController.thumbPage ...你有没有: 干得好 删除构建目录 如果这不起作用,我会搜索项目或project.pbxproj文件(只使用文本编辑器)并查看是否可以找到将添加到包含此目标的目标的对象或2个框架。 如果这不起作用,请吹掉.xcodeproj目录,然后创建一个新目录并将所有文件添加到其中......但这是最后的手段。 Turns out that my "Comp ...
  • 警告:我是从我没有的专业知识水平说话的。 鉴于这篇文章的长度,我可能错误了很多次。 TL; DR:顶级价值的生存期是逆变的。 参考值的寿命是不变的。 介绍问题 你可以通过将VecRef<'a>替换&'a mut T VecRef<'a>显着简化你的例子。 此外,应该删除main ,因为与某些特定的生命实例相比,讨论函数的一般行为更完整。 而不是VecRefRef的构造函数,让我们使用这个函数: fn use_same_ref_ref<'c>(reference: &'c mut &'c mut ()) {} ...
  • 我不认为你的任何一种方法都是最好的解决方案。 您可以使用char本身作为HashMap的键,无需将其转换为String: fn do_something(word:&str) -> usize { let mut seen_chars = HashMap::new(); let mut answer : usize = 0; for (i,c) in word.chars().enumerate() { match seen_chars.get(&c) { ...
  • Rust正确地防止了这里的错误。 你的get_val函数试图将引用返回到HASHMAP ,但这不安全,除非你继续持有锁,否则其他人可能会来修改你脚下的地图。 您需要保持锁定,直到返回值(通过返回互斥锁以及值;例如,使用MutexGuardRef中的owning_ref ),或复制值而不是返回引用: pub fn get_val(k: &str) -> Option { HASHMAP.lock().unwrap().get(k).cloned() } 有了这个实现, Foo当然需要实现C ...
  • 这确实不同于在迭代递归结构时无法获得可变引用:不能一次多次借用可变引用 。 如果我们查看那里的答案,修改了一下,我们可以看到它匹配一个值,并且能够返回终端案例中匹配的值。 也就是说,返回值是一个Option : fn back(&mut self) -> &mut Option> { let mut anchor = &mut self.root; loop { match {anchor} { &mut Some(ref mut ...
  • 没有区别。 在第二部分中,您将重新绑定本地名称a ,而不是改变它指向的对象。 There is no difference. In the second part you are rebinding the local name a, not mutating the object it points to.
  • 在对方法set的调用中,使用impl块上的lifetime参数,并且为方法签名中的'a填充w的值的生存期。 否。生命周期参数'a的值在创建Foo结构时是固定的,并且永远不会改变,因为它是其类型的一部分。 在您的情况下,编译器实际上选择'a与v和w的生命周期兼容的值。 如果那是不可能的,它将失败,例如在这个例子中: fn main() { let v = 5; let mut f = Foo { x: &v }; println!("f is {:?}", f); let w ...
  • 问题出现是因为不可变引用是其(引用)类型的变体,而可变引用对其类型是不变的。 理解这个概念的一个很好的读物是Nomicon 。 HashMap: get与get_mut 进一步缩小,这是一个简单的代码重现问题: #![allow(unused_variables)] use std::collections::HashMap; fn main() { let mut hm = HashMap::<&u32, u32>::new(); // --+ 'a let c = 42; ...
  • 当你写fn dostuff(&'a mut self)你强制要求对self的引用必须至少与生命周期'a一样长。 但它与您在Test结构的定义中使用的相同。 这意味着dostuff调用者必须在整个test生命周期中self借助。 在dostuff()之后,现在借用self并且在test被删除之前借用不会完成。 根据定义,您只能调用该函数一次,因此无法在循环中调用它。 我需要修复仍然保持功能签名相同 所以,你现在应该明白这是一个不可能的要求。 您既可以使用函数签名,也可以循环调用它。 你不能兼得。 When y ...
  • JavaScript是按照DOM中遇到script标记的顺序执行的(默认情况下) - 如果在加载其依赖项之前加载某个具有依赖项的库,则会出现错误(如您所发现的) 请参阅: http : //www.whatwg.org/specs/web-apps/current-work/multipage/scripting-1.html#attr-script-async 使用这些属性可以选择三种可能的模式。 如果存在async属性,则脚本一旦可用就会异步执行。 如果async属性不存在但是存在defer属性,则在页 ...

相关文章

更多

最新问答

更多
  • python的访问器方法有哪些
  • 使用Zend Framework 2中的JOIN sql检索数据(Retrieve data using JOIN sql in Zend Framework 2)
  • 透明度错误IE11(Transparency bug IE11)
  • linux的基本操作命令。。。
  • 响应navi重叠h1和nav上的h1链接不起作用(Responsive navi overlaps h1 and navi links on h1 isn't working)
  • 在C中读取文件:“r”和“a +”标志的不同行为(Reading a File in C: different behavior for “r” and “a+” flags)
  • NFC提供什么样的带宽?(What Kind of Bandwidth does NFC Provide?)
  • 元素上的盒子阴影行为(box-shadow behaviour on elements)
  • Laravel检查是否存在记录(Laravel Checking If a Record Exists)
  • 设置base64图像的大小javascript - angularjs(set size of a base64 image javascript - angularjs)
  • 想学Linux 运维 深圳有哪个培训机构好一点
  • 为什么有时不需要在lambda中捕获一个常量变量?(Why is a const variable sometimes not required to be captured in a lambda?)
  • 在Framework 3.5中使用服务器标签<%=%>设置Visible属性(Set Visible property with server tag <%= %> in Framework 3.5)
  • AdoNetAppender中的log4net连接类型无效(log4net connection type invalid in AdoNetAppender)
  • 错误:发送后无法设置标题。(Error: Can't set headers after they are sent. authentication system)
  • 等待EC2实例重启(Wait for an EC2 instance to reboot)
  • 如何在红宝石中使用正则表达式?(How to do this in regex in ruby?)
  • 使用鼠标在OpenGL GLUT中绘制多边形(Draw a polygon in OpenGL GLUT with mouse)
  • 江民杀毒软件的KSysnon.sys模块是什么东西?
  • 处理器在传递到add_xpath()或add_value()时调用了什么顺序?(What order are processors called when passed into add_xpath() or add_value()?)
  • sp_updatestats是否导致SQL Server 2005中无法访问表?(Does sp_updatestats cause tables to be inaccessible in SQL Server 2005?)
  • 如何创建一个可以与持续运行的服务交互的CLI,类似于MySQL的shell?(How to create a CLI that can interact with a continuously running service, similar to MySQL's shell?)
  • AESGCM解密失败的MAC(AESGCM decryption failing with MAC)
  • SQL查询,其中字段不包含$ x(SQL Query Where Field DOES NOT Contain $x)
  • PerSession与PerCall(PerSession vs. PerCall)
  • C#:有两个构造函数的对象:如何限制哪些属性设置在一起?(C#: Object having two constructors: how to limit which properties are set together?)
  • 平衡一个精灵(Balancing a sprite)
  • n2cms Asp.net在“文件”菜单上给出错误(文件管理器)(n2cms Asp.net give error on Files menu (File Manager))
  • Zurb Foundation 4 - 嵌套网格对齐问题(Zurb Foundation 4 - Nested grid alignment issues)
  • 湖北京山哪里有修平板计算机的