Как может адрес памяти структуры быть одинаковым внутри функции и после того, как структура была возвращена?

Фраза, когда из области видимости выходят значения, автоматически извлекаемые из стека, повторяется много раз, но приведенный здесь пример опровергает утверждение:

fn main() {
    let foo = foobar();
    println!("The address in main {:p}", &foo);
}

fn foobar() -> Employee {
    let emp = Employee {
        company: String::from("xyz"),
        name: String::from("somename"),
        age: 50,
    };
    println!("The address inside func {:p}", &emp);
    emp
}

#[derive(Debug)]
struct Employee {
    name: String,
    company: String,
    age: u32,
}

Результат:

The address inside func 0x7fffc34011e8
The address in main 0x7fffc34011e8

Это имеет смысл. Когда я использую Box для создания структуры, адрес отличается, как я ожидал.

  1. Если функция возвращает право собственности (перемещение) возвращаемого значения вызывающей стороне, то после выполнения функции память, соответствующая этой функции, выскакивает, что небезопасно, тогда как структура, созданная внутри функции, становится доступной даже после выхода из функции?
  2. То же самое происходит при возврате массива. Где эти элементы хранятся в памяти, в стеке или в куче?
  3. Будет ли компилятор выполнять анализ выхода во время компиляции и перемещать значения в кучу, как это делает Go?

Я уверен, что Employee не реализует черту Copy.


person chellathurai    schedule 15.09.2020    source источник
comment
повторяется много раз - необходима ссылка   -  person Shepmaster    schedule 15.09.2020
comment
Быть извлеченным из стека абстрактной машины и копироваться в новое место в стеке при фактическом выполнении на реальном оборудовании - это две разные вещи. Вы не можете ничего доказать или опровергнуть в поведении абстрактной машины, запустив код. Нет никаких оснований предполагать, что адрес значения будет изменен, когда оно будет передано в функцию или возвращено из нее. Указатели не являются целыми числами, и такие языки, как Rust и C не являются языками низкого уровня.   -  person trentcl    schedule 15.09.2020


Ответы (1)


Во многих языках переменные - это просто удобное средство для людей назвать некоторые значения. Даже если с логической точки зрения мы можем предположить, что существует одно конкретное хранилище для каждой конкретной переменной, и мы можем рассуждать об этом с точки зрения копирования, перемещения ... это не означает, что эти копии и перемещает физически случаются (и особенно из-за оптимизатора). Более того, читая различные документы о Rust, мы часто находим термин привязка вместо variable; это усиливает идею о том, что мы просто ссылаемся на значение, которое где-то существует. Это в точности то же самое, что писать let a=something();, затем let b=a;, снова _3 _... мы просто меняем свое мнение по поводу имени, но на самом деле никакие данные не перемещаются.

Когда дело доходит до отладки, сгенерированный код обычно неоптимален, поскольку каждой переменной предоставляется собственное хранилище для проверки этих переменных в памяти. Это может ввести в заблуждение относительно истинной природы оптимизированного кода.

Возвращаясь к вашему примеру, вы обнаружили, что Rust решил выполнить своего рода оптимизацию возвращаемого значения (распространенный в настоящее время термин C ++), потому что он знает, что временное значение должно появиться в вызывающем контексте, чтобы предоставить результат , и этот результат получается из локальной переменной внутри функции. Итак, вместо создания двух разных хранилищ и копирования или перемещения от одного к другому, лучше использовать одно и то же хранилище: локальная переменная сохраняется вне функции (там, где ожидается результат). С логической точки зрения это ничего не меняет, но намного эффективнее. И когда в игру вступает встраивание кода, никто не может предсказать, где на самом деле хранятся наши переменные / значения / привязки.


В некоторых комментариях ниже говорится, что на эту оптимизацию возвращаемого значения можно рассчитывать, поскольку она происходит в Rust ABI. (Я этого не знал, еще новичок; ^)

person prog-fh    schedule 15.09.2020
comment
Это означает, что когда оптимизатор находит возвращаемое значение в функции, он создает это значение в стеке, которое не уничтожает даже выход из функции и указывает на эту память, верно? - person chellathurai; 15.09.2020
comment
@chellathurai Employees пространство выделяется в стековом фрейме mains, а не foobars. - person Shepmaster; 15.09.2020
comment
@chellathurai да, и если вы знаете C, это все равно что предоставить функции с параметром указателя, чтобы сохранить результат в вызывающем контексте (даже если ничто не гарантирует, что это точно произойдет). - person prog-fh; 15.09.2020
comment
можете ли вы предоставить какой-либо источник, который демистифицирует это пространство сотрудников выделяется во фрейме основного стека, а не в foobar - person chellathurai; 15.09.2020
comment
@ prog-fh yah, это просто для моего понимания, поэтому я не могу найти больше источников, связанных с этим. - person chellathurai; 15.09.2020
comment
не обязательно должно быть - это не так. См. Мой предыдущий комментарий - person Shepmaster; 15.09.2020
comment
Вы читали ссылку Шепмастера? Могу ли я эффективно вернуть объект по значению в Rust? - person John Kugelman; 15.09.2020
comment
@ prog-fh Вероятно, не слишком много источников, поскольку Rust ABI не стабилизирован, однако его концепция та же, что и в C ++ Itanium ABI: если [... критерии ...] вызывающий передает адрес как неявный параметр. Затем вызываемый объект создает возвращаемое значение по этому адресу. - person kmdreko; 15.09.2020
comment
@JohnKugelman да, я читал это, в нем подробно рассказывается о том, как оптимизируются материалы нижнего уровня в этом случае. - person chellathurai; 15.09.2020