LINUX.ORG.RU

Нубских вопросов по Rust'у тред

 , , ,


1

4

Привет, ЛОР. Сразу попрошу камнями не кидаться, ибо за плечами 5 лет Java разработки, возможно она и накладывает такой отпечаток на восприятие нового языка. Энивей, ближе к делу. Читаю их оффициальную книгу и есть там такой пример

fn main() {
    println!("Guess the number!");

    println!("Please input your guess.");

    let mut guess = String::new();

    io::stdin().read_line(&mut guess)
        .expect("Failed to read line");

    println!("You guessed: {}", guess);
}
И мне взрывает мозг конструкция с mut, точнее я вроде как это прекрасно понял (но хочу убедиться что понял правильно, потому и пишу), + в книге дикая путаница в важных вещах, которые еще и обзываются по разному (binding, variable, reference).

Как я это вижу:

let mut guess = String::new();
На языке джависта «Создаёт мутабельную ссылку на мутабельный объект». Т.е. меняться может как то, куда она указывает, так и объект, на который она указывает (можно вызвать guess.push('c');, например)

let guess = String::new();

Это же звучит как «Создает иммутабельную ссылку на иммутабельный объект», т.е. нельзя поменять то, куда она указывает, равно как и значение по ней (нельзя вызвать guess.push('c');)

let guess = &mut String::new();

А вот это просто вишенка на торте, а может быть и сам торт. Создаёт иммутабельную ссылку на мутабельный объект. Т.е. мы не можем поменять то, куда оно указывает, но можем изменять значение (вызовом того же guess.push('c');) Если описывать последнее терминами раста, то создаёт иммутабельный биндинг на мутабельную ссылку.

И если я понял это правильно, то у меня вопрос, почему в примере книги написано вот так?

    let mut guess = String::new();

    io::stdin().read_line(&mut guess)
        .expect("Failed to read line");

Ибо мне как джависту очень хочется написать вот так

    let guess = &mut String::new();

    io::stdin().read_line(guess)
        .expect("Failed to read line");
Т.е. биндинг сам по себе иммутабелен и всегда указывает на одну ссылку, а ссылка мутабельна и её содержимое может меняться. Вот, дискасс, если я что-то упустил или не понял - поясните пожалуйста.

★★★★

Последнее исправление: Jefail (всего исправлений: 1)

На языке джависта «Создаёт мутабельную ссылку на мутабельный объект».

Нет. Это не ссылка, а мутабельный объект.

Создаёт иммутабельную ссылку на мутабельный объект.

Нет. Это «создаём объект и берём мутабельную ссылку на него».

Т.е. мы не можем поменять то, куда оно указывает

Ссылки не указатели.

Ибо мне как джависту очень хочется написать вот так

Разницы никакой. Это одно и то же.

RazrFalcon ★★★★★
()
Последнее исправление: RazrFalcon (всего исправлений: 1)
Ответ на: комментарий от hippi90

Анонимная переменная.

Фактически там

let anon = String::new();
let guess = &mut anon;

trex6 ★★★★★
()
Последнее исправление: trex6 (всего исправлений: 1)

Создает объект типа String на стеке

let mut guess = String::new();

Передает мутабельную ссылку на созданный объект в функцию.

io::stdin().read_line(&mut guess)

В один момент на один и тот же объект может быть создана ровно одна мутабельная ссылка (если сам объект мутабелен, иначе, очевидно, ни одной), либо произвольное число иммутабельных. Ссылки не могут пережить исходный объект. Объект, на который взята ссылка, является ‘borrowed’. Borrow checker проверяет соответствие того, как взяты ссылки и времен их жизни правилам. Вся эта муть в конечном счете сделана для того, чтобы гарантировать отсутствие неопределенного поведения и гонок данных, ликвидирует класс ошибок (dangling references, use-after-free, логические)

Siborgium ★★★★★
()
let mut guess = String::new();

Создает не ссылку, а создает новый объект типа String, который связывает с «переменной» guess, которую можно передавать по ссылке «&mut», которая позволяет менять ссылаемый объект, а также просто по ссылке «&». Можно еще полностью передать владение объектом. Более того, можно поменять объект, завладев другим, а этот String отправив в утиль.

let guess = String::new();

Создает новый объект типа String, который связывает с переменной guess, которую можно передавать или как с передачей полного владения (можно менять объект и делать с ним что угодно), или как ссылку «&», которая не позволяет менять объект.

let guess = &mut String::new();

Вот здесь сначала создается объект типа String, потом из него получается ссылка на объект, и эта ссылка уже связывается с переменной guess. Переменная здесь действительно становится не объектом, а ссылкой, по которой можно менять объект. Саму ссылку менять нельзя здесь.

Если бы хотел менять еще и ссылку, то было бы вообще так:

let mut guess = &mut String::new();

И, кстати, твой пример компилируется.

use std::io;

fn main()
{
    let guess = &mut String::new();

    io::stdin().read_line(guess)
        .expect("Failed to read line");
}

Скорее всего, ты на правильном пути.

А в книге написано, возможно, так, потому что guess у них владеет объектом, а у тебя ссылка, даже изменяемая - нет, не владеет. У них больше возможностей для движения. Ты не можешь передать владение объектом, а они могут.

dave ★★★★★
()

Просто нужно понимать, что в rust нет сборщика мусора. Очень важно, чтобы у каждого объекта был владелец. Когда владелец теряется, то объект немедленно удаляется (есть хак, чтобы не удалять, что важно для связки с кодом на Си).

В твоем примере у объекта владельцем является блок. При выходе из блока объект будет немедленно удален. Поэтому все твои ссылки не могут пережить блока, а значит, толку от них не так и много может быть.

В случае примера из книги, владение объектом может быть передано дальше, а значит, и объект может продолжить свою жизнь за пределами блока.

dave ★★★★★
()
Последнее исправление: dave (всего исправлений: 1)
Ответ на: комментарий от RazrFalcon

Как оно внутри устроенно - я хз

Хороший годный эксперт.

bread
()
Ответ на: комментарий от RazrFalcon

Нет. Это не ссылка, а мутабельный объект.

В расте нету объектов.

Это не C++.

freddie_sadler
()
Ответ на: комментарий от hippi90

Кто в данном случае владеет значением? Ссылка же не владеющая.

Никто не владеет. В расте модель как при ГЦ на подсчёте ссылок. Сам сторедж существует отдельно от ссылки и ею не управляется. Вернее управляется, но косвенно. Все переменные - ссылочные. Сторедж не постоянный и может копироваться сколько угодно.

Шаринг данными крайне ограничен и из-за этого ограничения можно посчитать кол-во ссылок. Инвариант простой - rw доступ может быть только по одной ссылки - других ссылок к стореджу быть не должно. А значит при выходе из скоупа ссылки на мутабельный сторедж - его можно снести.

Т.е. любая ссылка на мутабельный сторедж - владелец, т.к. она может быть всегда одна, а значит может со стореджом делать что угодно.

freddie_sadler
()
Ответ на: комментарий от Siborgium

Создает объект типа String на стеке

Нет, guess не объект.

В один момент на один и тот же объект может быть создана ровно одна мутабельная ссылка

И эта ссылка и есть guess

(если сам объект мутабелен, иначе, очевидно, ни одной)

Сторедж не может быть мутабельным - его нельзя будет дропнуть.

Borrow checker проверяет соответствие того, как взяты ссылки и времен их жизни правилам.

Нихрена. Там ничего ненужно проверять. Просто следи за кол-вом ссылок. Все лайфтаймы пишутся руками.

Вся эта муть в конечном счете сделана для того, чтобы гарантировать отсутствие неопределенного поведения и гонок данных, ликвидирует класс ошибок (dangling references, use-after-free, логические)

Нет, вся эта муть сделана для того, что-бы быть как можно проще за счёт перекидывания всей работы на плечи адептов. Ну и в целом язык предполагал ГЦ и был с ГЦ. Другая модель сюда не интегрируется. Здесь попросту нету переменных, т.е. value-типов за исключением базовых.

К тому же, она никакие конки не ликвидирует. Ликвидирует их запрет на шаринг(который можно обойти, и который множество раз обходится даже в базовых примитивах stdlib). Запрети шаринг где угодно - никаких гонок не будет.

dangling references, use-after-free, логические)

Это туда же. Запрет шаринга решает все проблемы. К тому же - это враньё. Ты не можешь решить проблему use-after-free - ты попросту не используешь аллокации. Всё это unsafe, а в unsafe никаких гарантий нет.

Таким образом - сколько там use-after-free в твоей String - никто не знает.

freddie_sadler
()
Ответ на: комментарий от dave

Огромное спасибо за этот и предыдущий комментарий. Видимо владение и вот эта логика описаны где-то дальше в книге. Вопрос можно считать закрытым.

Jefail ★★★★
() автор топика
Ответ на: комментарий от freddie_sadler

В компиляторе может оно и считает ссылки, компилируя, я хз, но ты сейчас очень запутаешь новичка. Когда програма работает, то нету никаких счетчиков ссылок если ты их не создашь через Rc/Arc. То что у ТСа без счетчиков

vertexua ★★★★★
()
Ответ на: комментарий от vertexua

В компиляторе может оно и считает ссылки, компилируя, я хз, но ты сейчас очень запутаешь новичка.

Именно эту базу и нужно понимать, что-бы понимать с чем работаешь - очевидно.

Когда програма работает, то нету никаких счетчиков ссылок

Множество нюансов переносится в рантайм. В том числе и миграция стореджа, оверхед вызванный ограничением шаринга, отсутствие возможности управления стореджом.

То что у ТСа без счетчиков

Это неважно. Я говорил не о том, что есть, а том какую семантику предоставляет «язык». Там «снизу» может быть что угодно - это неважно, повторюсь.

freddie_sadler
()
Ответ на: комментарий от freddie_sadler

Сам противоречишь, «надо понимать», а потом «семантика и неважно что внизу».

Мне кажется что если вслух произнести счётчик ссылок, то поголовно все подумают что это в рантайме. Потому важно уточнять, а то будут потом рассказывать что Rust как Python работает

vertexua ★★★★★
()
Последнее исправление: vertexua (всего исправлений: 1)
Ответ на: комментарий от vertexua

Сам противоречишь, «надо понимать», а потом «семантика и неважно что внизу».

Очевидно, что нужно и то и то. Но семантика сверху куда важнее нюансов снизу, вернее она более понятна обывателю. Т.к. минимум она ему сообщает о нюансах работы, с которыми он может(хотя почему может) столкнутся.

Ограничения шаринга - это фундаментально. То, что сторедж неуправляемый и висит где угодно - на этом даже сами адепты погорели в попытке запилить async.

а то будут потом рассказывать что Rust как Python работает

Ну питон питон не потому, что там есть гц. А так же - семантика та же. Ты точно так же не знаешь где сторедж у твоих объектов и все переменные - ссылки.

freddie_sadler
()
Ответ на: комментарий от RazrFalcon

Ну да, такие адепты у этого «языка». И это не моя вина, что вы сами ничего не знаете про объект своего обожания.

В целом понятно - почему так происходит. Пропаганде невыгодно, что-бы ты что-то знал. Чем больше знаешь - тем сложнее тебя обмануть. А если нельзя тебя обмануть - как можно заставить тебя верить в том, чего в реальности нет.

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

freddie_sadler
()
Ответ на: комментарий от vertexua

Нет, хочет что-бы данная секта порождала меньше фанатиков, угнетающих адекватность среды.

freddie_sadler
()
Ответ на: комментарий от vertexua

Сдается мне, что наш завсегдатай опять гонит какую-то пургу. Если и используется неявный счетчик ссылок в реализации, то крайне редко. В обычном коде такой счетчик часто просто не нужен. Полагаю, компилятор не настолько туп, чтобы подставлять счетчики там, где они явно излишни.

dave ★★★★★
()
Ответ на: комментарий от RazrFalcon

Обоснуешь за этот трёп? Или сказать нечего и началось типичное для адептов «ты неправ потому что я так сказал»?

freddie_sadler
()
Ответ на: комментарий от dave

Меня не перестаёт удивлять уровень(вернее его полное отсутствие) состоятельности тутошних экспертов. Откуда данный адепт взял какие-то счётчики и прочее? Неясно - придумал и выдал за мои тезисы.

Полагаю, компилятор не настолько туп, чтобы подставлять счетчики там, где они явно излишни.

Компилятора там нет. Но тот огрызок фронта, что есть - действительно настолько туп. Правда там ничего и ненужно вставлять.

Он просто смотрит на все ссылки в рамках скоупа. Все ссылки которые выходят за скоуп - живут лишь в рамках выражения. Если это не так - тебе нужно руками прописывать лайфтаймы.

Ты не можешь просто так передать ссылку в функцию и где-то её сохранить. Поэтому огрызок считает, что это просто временная ссылка.

Руками ты прописываешь связи между стореджами. Всегда. А это значит, что сохранить ты ссылку можешь только в том, что выше по скоупу, либо в рамках этого скоупа.

Всё, подобная модель не требует какого-либо чекера, либо подсчёта чего-то.

freddie_sadler
()
Ответ на: комментарий от RazrFalcon

Это наш царюшка царь Нужна помочь владельцев core2 в измерении производительности программы, вот это связано с парашей у него в блоге

Мы, как истинные почитатели царя, ждем его блог написаный С++

vertexua ★★★★★
()
Последнее исправление: vertexua (всего исправлений: 1)
Ответ на: комментарий от dave

Я думаю он алгоритм борроу-чекера называет счетчиком, который «считает» чтобы было не больше мутабельной одной ссылки. Сильнейший способ напугать всех счетчиками ссылок в рантайме, которые по дефолту подразумевается, когда говорят «счетчик»

vertexua ★★★★★
()
Ответ на: комментарий от freddie_sadler

Когда блог, болтун? Побежал показывать

vertexua ★★★★★
()
Ответ на: комментарий от freddie_sadler

Слушай, раз ты зашел сюда, где про std::variant прочитать? Насколько аналогом будет суммирующих типов (или как называются sum types)? Понятное дело, union нельзя использовать, если есть классы.

dave ★★★★★
()
Ответ на: комментарий от freddie_sadler

Точнее, не хочется использовать union как в примере из 4-го издания Страуструпа.

dave ★★★★★
()
Ответ на: комментарий от dave

Слушай, раз ты зашел сюда, где про std::variant прочитать?

Тут https://en.cppreference.com/w/cpp/utility/variant и тут https://en.cppreference.com/w/cpp/utility/variant/visit

Насколько аналогом будет суммирующих типов (или как называются sum types)? Понятное дело, union нельзя использовать, если есть классы.

Ну сумтипы в разных контекстах значат разное. В недоязычках это в основном про выделение общих признаков. Но там это нужно потому, что сумтип затирает тип.

В C++ же(и как следствие в варианте) система типов на порядок мощнее недоязычков и не теряет типы. Таким образом x внутри лямбды visit([](auto x) {}, sumt); будет тем самым интерфейсом предоставляющим общие признаки, но и не только.

На самом же деле это не так и нужно осознать разницу. Здесь нет слияния(объединения типов) на уровне x - она так и остаются разные. Когда как в недоязычки - сумтип - это тип.

Т.е. сам вариант похож на сумтип в недоязычках и сам по себе он не обладает нужными свойстами(он не предоставляет доступ к общим признакам), но это предоставляет visit.

По-сути это полный аналог, но куда более мощный. Единственное с чего могут зайти адепты надоязычков - это со всякого мусорного сахара поверх сумтипов. Но всё это дристня в сравнении с тем, что может С++.

freddie_sadler
()
Ответ на: комментарий от freddie_sadler

Это я видел. Ладно, придется вникать по-видимому в эти особенности. А свежих книг нет по варианту, концептам и тому подобном? На английском даже лучше.

dave ★★★★★
()
Ответ на: комментарий от anonymous

У меня мало статистики для «что/бы» - поэтому пишу как хочу. Будет - буду писать нормально.

freddie_sadler
()
Ответ на: комментарий от dave

std::variant такой sum type, что лучше без него. Даже exhaustive не умеет.

Про конструктор по умолчанию лучше вообще не вспоминать.

RazrFalcon ★★★★★
()
Последнее исправление: RazrFalcon (всего исправлений: 1)
Ответ на: комментарий от dave

Это я видел.

Ну ты напиши что хочешь и как использовать.

Ладно, придется вникать по-видимому в эти особенности.

Ну это базовые свойства языка. Никто их не описывает каждый раз для каждого примитива.

А свежих книг нет по варианту, концептам и тому подобном? На английском даже лучше.

Не знаю - не читаю. Тут нужно другое читать/вникать. Те же концепты это то, что прямо следует из семантики С++.

Читать следует именно всякие левые заморочки. Там какой синтаксис, какие кейворды, куда что можно писать. Правда всё это в С++ достаточно интуитивно. Это (уже) не помойка как недоязычки.

Такие нюансы я смотрю в доках, а что касается языка и фишек - мониторю состояние того же шланга и смотрю тесты. Так же там есть ссылки на доки.

Ничего, кроме практики, нету. Правда эта практика должна быть с мотивацией делать лучше. Допустим - не терять типы. И в процессе само поймётся что, зачем и почему.

Тоже самое и с концептами. Когда нужно будет реализовать шаблонную функцию для отдельного подмножества типов. Там уже будет те проблемы, которые нужно будет понять и будет обратная связь - получилось - понял, а нет - не понял.

Каждая фича она решает определённую задачу, до которой пацаны дошли. И сложно понять назначение до тех пор, пока не столкнёшься с той же задачей/тем же классом задач.

freddie_sadler
()
Ответ на: комментарий от freddie_sadler

Тогда еще вопросы. В пантеоне плюсовых компиляторов clang какое место занимает по эффективности генерируемого кода?

И работал ли с нашими отечественными компиляторами? Знаю, что в одном есть поддержка C++11. А есть ли С++17?

dave ★★★★★
()
Ответ на: комментарий от RazrFalcon

Ах да, я тут вспомнил одну проблему. Не знаю почему так вышло и как ты связан с нею, но всё же.

Судя по моим наблюдениям - множество адептов раста и экспертов в крестах - это qt-макаки. Очевидно, что qt-макака ничего не знает про язык, про матчасть. Её задача накидывать кнопочки на формочку. И вот я до сих пор не понимаю - почему и что движет этими макаками, когда они позволяют себе какие-то заявления и начинают что-то из себя строить.

freddie_sadler
()
Ответ на: комментарий от dave

Тогда еще вопросы. В пантеоне плюсовых компиляторов clang какое место занимает по эффективности генерируемого кода?

Ну их всего два - гцц/шланг. Шланг как компилятор - говно говна. Его следует воспринимать просто как инструментарий для работы с языком.

Даже не смотря на то, что в него поток входа намного ниже, чем в крестовый фронт гцц - он мёртвый. Но гцц это чисто компилятор со всеми вытекающими.

Шланг/ллвм обладают необходимым функционалом для сборки рядового дерьма. Причём именно для дерьма он настроен по умолчанию лучше. Но в данном кейсе от оптимизатора ничего не требуется.

Так же, он намного(до 2-3 раз) медленнее гцц.

Можно зайти в мою тему, и там есть портянка. В ней шланг сливает в 10 раз, потому как слишком слаб. И он слаб в любых подобных кейсах. Сам кодоген там тоже слабее, но если рядовое хайлевел говно - покатит и от шланга.

И работал ли с нашими отечественными компиляторами? Знаю, что в одном есть поддержка C++11. А есть ли С++17?

Нет.

freddie_sadler
()
Ответ на: комментарий от freddie_sadler

Лично я использую сразу два в ситуации, если говорить об обычном С++.

Шланг встроен в ide, а собираю я gcc. gcc куда быстрее собирает, куда информативнее(в большинстве случае). Если gcc не обладает нужным беком(допустим wasm, либо(судя по словам пацанов) там arm-бек слабый), то собираю под нужный таргет уже шлангом.

freddie_sadler
()
Ответ на: комментарий от dave

Это всё не компиляторы С++. Маздайское говно попросту мусор и компилятором в принципе не является. Оно мало того, что не умеет в С++, а в то, что умеет - всё это работает через жопу.

Интеловский же компилятор - это локальные, вендорские заморочки интела. В актуальный С++ он вообще не может. Смысла использовать его как компилятор общего назначения - нету.

Т.е. даже фактически они не являются компиляторами С++ в том контексте, в котором его понимаю я, да и не только. А понимаю я под С++ актуальный С++. В него не может ничего из вышеперечисленного.

Насколько я слышал - есть какие-то фронты, которые умеют в актуальный С++, но это не компиляторы и не «умеют» это лишь со слов рекламных агиток.

freddie_sadler
()
Ответ на: комментарий от dave

Я уже показывал, но в этом фрагменте вся суть маздайского говна: https://www.youtube.com/watch?v=m9tcmTjGeho&feature=youtu.be&t=3689

Кстати, тут адепт врёт. Он использует здесь в сравнении старую версию гцц и новую шланга. И если шланг с каждой версией только регрессирует, то гцц ускоряется. На всех этих бенчах(я проверил лично) гцц уделывает шланг.

freddie_sadler
()
Ответ на: комментарий от dave

Вы поаккуратнее с вбросами царя. GCC хорош, но не лучше во всём. llvm/clang +/- на том же уровне. Ну и архитектурно он лучше, поэтому все плавно перелазят на него.

RazrFalcon ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.