Встав утром и посмотрев в профиль, отметил: на Хабре много лет, писал статьи, писал код. И ни разу не писал о коде на Хабре. А вообще-то разработчик. Поззорище! Пора исправляться.

Поговорим о классической (и болезненной) проблеме кодирования «присваивание вместо равенства» которая в любой момент может создать очень много проблем. О логическом источнике этой ошибке, и о способах решения.

Ну и ещё слегка вспомним «Звездные войны»:‑)


Проблема "диверсанта" внутри условия

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

<?php

$q=1;
//Do something big & wise
//Continue to do smth really cool
//Have a breakfast
if($q == 2 || $q = "foot bar"){
// Now we need to plant a tree
//
}

Несмотря на то, что ошибка известна почти всем - она всё равно появляется. И хуже всего то, как она себя ведёт в языках с динамической типизацией. По итогу тут мы получили неправильное ветвление: операция присваивания прошла успешно, а условие ($q=2) возвращает булево true, приведение к которому требуется внутри if().

А ещё, если не повезло - мы сбили счетчик. При этом заметить сразу-же такое получается далеко не всегда.

Попробую привести пример (погонял ИИ с просьбой сделать более-менее реалистичный код), где ошибка уже не так видна и очевидна:

<?php

// Имитируем получение настроек безопасности из Redis/API
$security_policy = ["min_delay" => 3600, "check_ip" => true, "alert_level" => "high"];
$raw_logs = ["last_attempt" => "2023-11-20 12:00:05", "source" => "internal"];

// Вычисляем интервалы в разных форматах для "шума"
$now = new DateTime();
$last_hit = DateTime::createFromFormat('Y-m-d H:i:s', $raw_logs['last_attempt']);
$since_last_hit = $now->getTimestamp() - $last_hit->getTimestamp();

$is_suspicious = ($since_last_hit < $security_policy['min_delay']);
$days_passed = floor($since_last_hit / (24 * 3600));
$formatted_diff = $now->diff($last_hit)->format('%R%a days %H:%I:%S');

// Данные пользователя
$user = [
    'id' => 777,
    'role' => 'guest',
    'is_verified' => false,
    'flags' => ['warned' => false, 'locked' => false]
];

if ($days_passed < 7 && $user['role'] == 'guest' || $user['is_verified'] = true) {
    
    // БАГ: $user['is_verified'] ТЕПЕРЬ ВСЕГДА TRUE.
    $report = "Security scan: " . $formatted_diff . " | Level: " . $security_policy['alert_level'];
    file_put_contents('security.log', $report . PHP_EOL, FILE_APPEND);
    
    echo "Action executed for user " . $user['id'];
}

// Проверяем последствия:
var_dump($user['is_verified']); // Выведет: bool(true) — мы только что сломали логику прав доступа

Поиск же подобного счастья (особенно когда это не первое условие) на том же JavaScript - может быть тем ещё развлечением. А особенно весело будет, если второе-третье условие окажется ещё и редко применяемым:

Пример подвоха на JS. Более тяжелый вариант

В JavaScript этот баг выглядит ещё «невиннее», потому что мы часто используем объекты и асинхронность. Когда код перегружен промисами, деструктуризацией и манипуляциями с датами, одиночное равно в конце длинной цепочки условий превращается в идеального диверсанта.

Вот пример с имитацией загрузки данных и проверкой прав доступа, где ошибка задвинута в самый край:

// Имитируем получение данных с сервера
const fetchLogData = async () => ({
    lastLogin: "2023-11-15T10:30:00Z",
    attempts: 3,
    metadata: { ip: "192.168.1.1", location: "MSK" }
});

const user = {
    id: 101,
    role: "guest",
    isPremium: false,
    permissions: ["read"]
};

async function checkAccess() {
    const log = await fetchLogData();
    
    // Работаем с датами: вычисляем, сколько часов назад был вход
    const now = new Date();
    const lastLoginDate = new Date(log.lastLogin);
    const hoursSinceLogin = Math.abs(now - lastLoginDate) / 36e5; // 36e5 = 60 * 60 * 1000

    const gracePeriod = 24 * 7; // неделя в часах
    const isLoginFresh = hoursSinceLogin < gracePeriod;

    // --- ЛОВУШКА ---  
    if (isLoginFresh && log.attempts < 5 && user.role === "guest" || user.isPremium = true) {
        
        // КАТАСТРОФА: 
        // 1. user.isPremium теперь ВСЕГДА true.
        // 2. Весь if теперь ВСЕГДА true (так как результат присваивания — true).

        console.log(`[AUTH] Access granted for ID ${user.id}.`);
        console.log(`[DEBUG] Session hours: ${hoursSinceLogin.toFixed(2)}`);
        
        grantTemporaryAccess(user);
    }
}

function grantTemporaryAccess(u) {
    console.log("Status check:", u.isPremium ? "PREMIUM" : "REGULAR");
}

checkAccess();

Ситуация может совсем выходить из под контроля, если мы попадаем во множественную вложенность, да ещё и с побочными эффектами в виде тернарных выражений.

Найдете без подсказки?

Пример более сложного в поиске на PHP подвоха
<?php

$session = [
    'user_id' => 1024,
    'permissions' => ['guest'],
    'is_admin' => false,
    'cache_ttl' => 3600
];

$auth_log = [
    ['ip' => '127.0.0.1', 'status' => 'ok'],
    ['ip' => '10.0.0.5', 'status' => 'fail']
];

// Генерируем коллбэк для "ленивой" проверки доступа
$check_access = function($u) use ($auth_log) {
    // Много шума: считаем количество неудачных попыток в логе
    $fails = array_reduce($auth_log, fn($acc, $item) => $acc + ($item['status'] == 'fail' ? 1 : 0), 0);
    return ($fails < 3); 
};

// --- ВОТ ОНО, КЛАДБИЩЕ ЛОГИКИ ---

$is_allowed = ($session['user_id'] > 0) 
    ? (function() use (&$session, $check_access) {
        
        // Вложенный тернарный оператор внутри замыкания...
        // ...перемешанный с вызовом функции...
        // ...и ГДЕ-ТО В КОНЦЕ МЫ СТРЕЛЯЕМ СЕБЕ В НОГУ:
        
        return $check_access($session) 
            ? (is_array($session['permissions']) && count($session['permissions']) > 0 && in_array($session['permissions'], $config_permissions)&& check_is_loagavaible($auth_log)  && $session['is_admin'] = true ) 
            : false;
            
      })() 
    : false;

// Результат:
echo $is_allowed ? "Доступ открыт" : "Доступ закрыт";
echo "\nСтатус админа теперь: " . ($session['is_admin'] ? "ДА" : "НЕТ"); 

Ошибка - в самом конце 32й строки.

Да, я понимаю что "хорошо написанное Unit-тестирование выловит это счастье". Но... даже не будем говорить о "плохом стиле" когда тестирования нет. Вы всегда пишите тесты строго параллельно самому коду?

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

И всегда проверяете буквально все возможные ответвления и приколы? Даже если второе (для экстремистов - 5е) условие будет выполняться в 1% случаев?

Почему такие ошибки, даже при опыте - всё же возможны и происходят. Смотрим внутрь вопроса

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

Этот феномен логично рассмотреть через концепции, созвучные «Критике чистого разума» Иммануила Канта.

Согласно Канту, мы и так заперты между двумя мирами: феноменальным (миром явлений, где всё зыбко, полно компромиссов множество шума) и ноуменальным («миром чистого разума» или «вещей в себе»), где царят абсолютные идеи и единство. Обычный человек постоянно балансирует между ними, пытаясь навязать хаосу реальности логические категории и свой порядок.

Однако разработчик, как заметил Фредерик Брукс в книге «Мифический человеко-месяц» (и позже развивали другие теоретики Computer Science), вынужден конструировать код, параллельно пребывая ещё и в третьем мире — мир машинной логики.

Конфликт миров и «ошибка присваивания»

  • Реальный мир (Мир компромиссов): Здесь мы оперируем контекстом. Мы знаем, что «пользователь активен» — это состояние, которое уже есть. В этом мире мы редко «присваиваем» значения сознательно; мы лишь констатируем факты.

  • Мир чистого разума (Мир абстракций): Здесь мы строим идеальные алгоритмы. Тут всё четко: если A = B то B = A. Здесь живет математическая гармония. К слову потому, что математика реального мира для оператора равенства работает в обе стороны одинаково.

  • Мир машинной логики (Мир формальных систем): Это мир предельного буквализма. Здесь символ = не означает «равенство» (как в чистом разуме), он означает «действие по изменению реальности». Также как и бинарные операторы - работают слева-направо.

Диссонанс: В реальном мире вопрос «Это яблоко — красное» является фиксацией факта. В машинном мире apple = red — это принудительная перекраска любого яблока в красный цвет.

Когда разработчик погружается в код, границы между мирами размываются. Возникает тот самый «когнитивный шум»: мозг начинает путать константы миров. Мы начинаем подсознательно ожидать, что машина «поймет» наше намерение из мира чистого разума, в то время как она тупо исполняет инструкцию из мира логики. С опытом мы, безусловно, допускаем эти ошибки реже. Однако с усталостью и перегрузкой - они всё равно проявляются.

Решение проблемы: вызываем конфликт миров

Аналог идей стиля Йода в других областях

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

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

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

В общем, однажды дико "застрелившись" на пару дней с одним (дико разросшимся условием) - мне удалось подсмотреть интересное решение, которое теперь применяю. И хочу поделиться с вами. Также, уже при подготовке этой статьи узнал (век живи, век - учись) что этот метод называется Условия Йоды (Yoda Conditions ). В общем - призываем на помощь гранд-мастера ордена Джедаев:

Вместо if ($q = 2) мы пишем условие в инвертированном виде: if (2 = $q), и тем самым создаем логический парадокс машинного кода. В мире "реальной математики" - ничего не меняется. А вот в коде - меняется всё.

  • Сравнивать в любом порядке - можно.

  • Присвоить нечто числу, строке, константе - нельзя.

<?php
define("WORLD_WELCOME", "Hello, world!");

$q = 1;

/**
 * Если мы случайно напишем (2 = $q), PHP сразу выдаст Fatal Error,
 * потому что нельзя присвоить значение литералу (числу).
 */

if (2 == $q || "foot bar" == $q || WORLD_WELCOME = $q) {
    // Теперь мы в безопасности. 
    // Если бы мы забыли второе "=", код бы просто не отвалился по ошибке.
    echo "Time to plant a tree.";
}

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

Из минусов такого подхода:

  • Поначалу вызывает диссонанс. Это немного непривычно читать.

  • При написании поначалу - совсем непривычно писать. Но быстро привыкаешь

  • Несколько нарушается единый стиль сравнения. if ('active' = var1 && var2 > 100500) - ну такое себе.

  • Пытался это победить методом полной инверсии типа: if (3 <= $a) (подразумевая что $a >= 3). Но от таких вот форм при неравенстве довольно быстро вскипает голова. Ибо сложно сообразить как написать: >= либо => . Ибо в голове сразу начинает инвертироваться и путаться почти всё.

Немного погонял ИИ. Он утверждает что на множестве других более современных языков - это стало куда как меньшей проблемой. Ибо на Python либо на Go запрещены присваивания внутри условий. А в Java либо C# - применим лишь частично.

И поэтому данный способ написания if("active" = valiable){} начал уходить в историю в конце 2010х, и сегодня - уже слабоактуален.

Что вы думаете об этом? Есть ли смысл в инверсном стиле (Yoda style) написания подобных выражений? Или этим лишь артефактом сегодня - лишь новичков на собеседованиях пугать?

Комментарии (40)


  1. apevzner
    09.02.2026 14:13

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

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

    И очень уж чудно это выглядит, if (2 == x) .... Может не стоит давать такие советы, как универсальные?

    P.S. Современные компиляторы выдают предупреждение, если случайно перепутать присваивание со сравнением. Идея включать предупреждения по максимуму и не игнорировать их - хорошая, годная.


    1. Newpson
      09.02.2026 14:13

      включать предупреждения по максимуму

      ...или просто использовать статический анализатор


      1. apevzner
        09.02.2026 14:13

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

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


        1. Newpson
          09.02.2026 14:13

          заодно с компиляцией

          Я писал код в таком режиме несколько лет, без всяких intellisense и статических анализаторов - просто в vim без плагинов: пишешь несколько сотен строк, потом (зачастую) читаешь портянку, которую выдаёт компилятор, потом исправляешь; и так в цикле, пока не будет ошибок и предупреждений; а ведь потом ещё отлаживать. Опыт не самый лучший, открыл для себя vscode, стало проще (да, жрущий электрон с плагинами на джаваскрипте) - всё в реальном времени: пишешь, сразу же исправляешь косяки свои; и тут же в git выделяешь нужные изменения, коммитишь и т.д.


          1. apevzner
            09.02.2026 14:13

            Я повесил в vim-е вызов команды make на hotkey, и после каждых нескольких строк его нажимаю.

            И да, я до сих пор пишу в vim.

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


            1. Newpson
              09.02.2026 14:13

              после каждых нескольких строк его нажимаю

              Почти как привычка сохранять файл у некоторых)

              Про системы сборки: для C и C++ стараюсь использовать CMake, где возможно (если проект сам в себе; если либы поддерживают или легко адаптируются под CMake), который уже генерирует Makefile или Ninja, где как лучше, а также compile_commands.json для clangd. С ним удобнее поддерживать зависимости в порядке и в целом проще иерархию проекта строить.

              Сейчас активно пробую Rust, там вообще уже всё предусмотрено - и пакетный менеджер, и система сборки, и статический анализатор, и даже тестирование; пока моё мнение складывается не в пользу C/С++ (по большей части).


              1. apevzner
                09.02.2026 14:13

                Почти как привычка сохранять файл у некоторых)

                А оно заодно и файл сохраняет

                Про системы сборки: для C и C++ стараюсь использовать CMake

                Если честно, я ненавижу cmake

                Сейчас активно пробую Rust, там вообще уже всё предусмотрено

                Мой второй язык - Go. Насколько я понимаю, там со сборкой, как в Rust. И очень подозреваю, что в Rust оно из Go и попало...


            1. DrSmile
              09.02.2026 14:13

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


              1. apevzner
                09.02.2026 14:13

                Не люблю автокомплит, если честно


    1. ZiNTeR Автор
      09.02.2026 14:13

      Ну в первую очередь речь идёт о языках-наследниках C, где имеется динамическая типизация. В C# и Java - применяемость ограничена.
      В PHP или в JS вы не получите ничего по ошибкам. Кроме как непонятно что делающего кода. Особенно это плохо когда условие редко исполняемое.
      В чистом Си, насколько помню (очень давно на нём писал) у вас в ряде компилятором действительно вылезет ошибка - самостоятельный typecast не случится, и булево значение в условии не появится.


      1. apevzner
        09.02.2026 14:13

        Ну, JS хоть и создан по принципу, "любая последовательность символов является синтаксически корректной программой", но присвоить значение переменной константе и он вроде не даст...


        1. winkyBrain
          09.02.2026 14:13

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


          1. SkiffCMC
            09.02.2026 14:13

            Ыменно, и да, ts + линтеры по идее должны такое где-то да поймать.


  1. Rsa97
    09.02.2026 14:13

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

    #include <stdio.h>
    
    int main() {
        int t = 0;
        if (t = 1) {
            printf("T is 1\n");
        } else {
            printf("T is not 1\n");
        }
    }
    

    И вот что говорит про этот код PVS-Studio:

    <source>:5:1: warning: V547 Expression 't = 1' is always true.
    <source>:5:1: warning: V559 Suspicious assignment inside the conditional expression of 'if' statement: t = 1.
    

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


    1. ZiNTeR Автор
      09.02.2026 14:13

      Безусловно при двух переменных - нам это не поможет. А может быть ещё больше запутает.
      Но в случаях с: конкретным значением, результатом другой функции, константой - оно сработает..


      1. Rsa97
        09.02.2026 14:13

        Сработает. Но статический анализ сработает лучше. А заодно и покажет другие потенциальные ошибки.


      1. Apoheliy
        09.02.2026 14:13

        Пардонь-те, но в списке хабов указан C++, поэтому:

        Одна из проблем инверсии в том, что результат (a == b) может быть не равен результату (b == a). В C++, особенно когда типы разные - легко. Поэтому начиная читать статью предполагал описание таких случаев и как от них уберечься, то есть меньше стрелять в ногу.

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

        Что касается упоминаний констант и результатов функции, то тут лучше быть поаккуратнее. Приведённый ниже код использует присваивание в константу и результат функции, он компилируется (но компилятор может насыпать предупреждений) и работает:

        #include <iostream>
        class A {
         public:
          A(int) {}
          const A& operator=(int) const { return *this; }
          operator bool() const { return true; }
        };
        int g_value = 0;
        int& funcB() {
          return g_value;
        }
        
        int main()
        {
          const A const1 = 5;
          int v1 = 10;
          if (const1 = v1) {
            std::cout << "success" << std::endl;
          }
          if (funcB() = v1) {
            std::cout << "success" << std::endl;
          }
          return 0;
        }
        


      1. Rsa97
        09.02.2026 14:13

        Кстати, в случае с функцией уже могут быть проблемы в порядке вычислений. Сравниваемая переменная может менять значение при вычислении функции и варианты a == f() и f() == a могут давать разный результат.


    1. Jijiki
      09.02.2026 14:13

      маленький код под С/С++ стат анализ ок, чуть больше я даже не понимаю контекста, я сколько не пробовал, эта шарманка начинает использовать проц(по моей оценке это спам нагрузкой цпу при вводе текста при анализе) на моей тачке, ради того чтобы проверить код, когда в Расте+(неупомянутый редактор текста) со статой и компиляцией всё ожидаемо(в этой связке нету спама - Раст+неупомянутый редактор текста) и очевидно (плата по озу очевидна, но редактор и Раст работают ожидаемо, в то время когда и вижуал студия, и вскод, и кути(даже иногда) грешат спамом нагрузки цпу при вводе текста)

      это в копилку визуал студии и С языков, разбираться с этим даже нету желания(всмысле что нагружает и поэтому отталкивает)

      в С++ до сих пор непонятно с модулями еще тоже, после раста отталкивает

      мой итог по моим наблюдениям: у раста анализ лучше и неупомянутого редактора нету спама нагрузкой цпу при большом проекте - это услада для меня, возращаюсь в С++ иногда - тесты делаю, всё неизменно, и после раста с модулями, да пускай неполноценная тема с модулями но удобно, С++ какой-то громоздкий уже кажется, а после попыток компиляций G3D, я вообще понял, что стандарты и любые новшества меняют экосистемы настолько - очень сильно кароче

      попробуйте собрать на генте G3D вы возможно пересмотрите что-то в софтовом подходе и создании чего-либо

      нужна ожидаемость, предсказуемость, понятная многопоточка, детерминизм для слоя программирования, и никаких icompile, каких-то флагов, которые не существуют, или ситуации как с питоном 2, только представьте сегодня мы кодим пользуемся всякими сборщиками, через время, экосистема изменится, никто даже разбираться не будет почему не собирается

      сегодня компайлер строгий через 20 лет станет снова добрый, тоже самое может ждать раст, но посмотрим, но кароче при всех прочих, что я видел, у С/С++ больше этой боли


      1. Andrey2008
        09.02.2026 14:13

        Теперь не понятно стало. То-ли человек не умеет мысли формулировать, то-ли и в комменты с llm пришли... :(


        1. Jijiki
          09.02.2026 14:13

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

          тоесть инструменты С/С++ работают неожидаемо, недетерминированно(а значит и многопоточка не интуитивная например - это же всё взаимосвязано получается), при вводе текста при анализе ведь получается если прагматически честно смотреть на это

          как бы это не звучало странно загружаем анриал енджин и пишем код(визуал студио/вскод/кути любой на выбор), будет там рекомпил на фоне, конечно, ведь так происходит проверка, почему так я даже знать не хочу

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

          тоесть мы вернёмся к вопросу доступности модулей, хорошо если они из коробки в инструменте, а так придётся компилировать интструмент - компилятор наверно, и еще вникать как завести модуль, когда в расте это делается проще

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


          1. Jijiki
            09.02.2026 14:13

            самое интересное при честном взгляде настройка LSP - это тоже неудобство, а не требование, вот и получается, что все эти надстройки над С/С++ уже в момент создания легаси получается


  1. webhamster
    09.02.2026 14:13

    Вопрос: что будет делать автор, если надо сравнить две переменных? В какой последовательности он будет их писать?

    А по факту, решений этой проблемы немного:

    • запретить выражение присваивания внутри условия: в императивных языках позволять присваивать в условии бессмысленно - ничего кроме усложнения чтения это не дает. Вместо линейного чтения человек вынужден прыгать в середину выражения, и раскручивать понимание "вширь". Ради чего такие упражнения?;

    • или обязать дополнять выражение присваивания внутри условия каким-то ключевым словом, которое говорит: я понимаю что я делаю;

    • или убрать из языка синтаксис присваивания, близко похожий на проверку условия;

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


    1. ZiNTeR Автор
      09.02.2026 14:13

      При двух переменных - вы лишь имеете право выбора, в какую из ног в случае чего будете стрелять :)
      Запрет присваивания (упомянул в статье) есть в Go и Python. Но не везде.
      Убрать синтаксис присваивания - невозможно по сути. У нас такое огромное наследие Си, что...
      Ну а вы бы сами тогда как бы обозначили бинарный оператор присваивания?

      Щас сюда набегут адепты сишо-плюсов, и будут кричать что

      Вероятно я сейчас за эти слова отхвачу по полной. Но... ничего страшного - пусть прибегают. Нужно же людям как-то развлекаться? :)


    1. apevzner
      09.02.2026 14:13

      Сейчас придут адепты старой школы, и скажут, что это - самый канонический способ написания копирования строк, завещанный нам отцами нашими Керниганом и Ричи:

      while (*d ++ = *s ++)
          ;
      


      1. kipar
        09.02.2026 14:13

        А потом нехороший человек затирает нулевой символ у s.


      1. webhamster
        09.02.2026 14:13

        while (*d ++ = *s ++);

        Как меня раздражают вот эти вот потуги писать код в виде ребусов, причем намеренно вводя пользователя в заблуждение. То есть, как бы говоря: не расслабляйся, давай-ка в очередной раз проверим твою внимательность и память, заодно потестируем твое умение справляться с когнитивными нагрузками! Ведь ты же этим должен заниматься когда программируешь на сишечке, других забот у тебя нет.

        Зачем объединять лексемы не в той последовательности, в которой они будут выполняться? Зачем прилеплять звездочку indirection к имени, а инкремент - писать отдельно? Потому что синтаксис позволяет? А почему бы не показать приоритет операций, чтобы не лезть в документацию? Ну напишите вы:

        while( *(d++) = *(s++) );

        и все сразу становится на свои места. Но нет, мы будем извращаться.


        1. apevzner
          09.02.2026 14:13

          В боевом коде я вот так вот напишу:

          while (*s != '\0') {
              *d = *s;
              s ++;
              d ++;
          }
          

          Потому что я зануда хочу избежать по возможности недопонимания среди возможных читателей


          1. Apoheliy
            09.02.2026 14:13

            Не, ты не зануда! Ты странный человек, так как код с ошибкой:

            Результирующая строка в d не будет закрыта нулевым символом.

            По факту получается, что:

            while (*d ++ = *s ++) ;

            написан правильнее.

            Сурпрайз!


  1. Anonimeba
    09.02.2026 14:13

    в VisualStudio в редакторе сразу выдаст предупреждение о недостижимости кода . смысл костылей ?


    1. ZiNTeR Автор
      09.02.2026 14:13

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

      Дважды в жизни мне приходилось экстренно - с телефона цепляться к боевому проекту по SSH с телефона, и подправлять косяки. В терминале, через nano.

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


      1. winkyBrain
        09.02.2026 14:13

        "Хорошо. А что будете делать, если вдруг окажетесь в ситуации что такого комбайна как телефон не будет под рукой?". Давайте и дальше выдумывать всякие почти невозможные события, чтобы подкрепить свои доводы, это же очень рабочий подход


  1. Andrey2008
    09.02.2026 14:13

    По крайней мере в С и C++ эта ошибка вымерла. Я пару лет назад статью на эту тему писал :)

     Объявляю ошибку вида if (x = 42) вымирающей и заношу её в Красную книгу C и C++ багов


    1. Jijiki
      09.02.2026 14:13

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

      когнитивная нагрузка в С языках всё еще выше, можно легко запутаться или что-то упустить

      это как фортнайт + слушать музыку на фоне, паралельно книжку читать, и чтоб всё по таймингу и без помарок, учитывая кеш и прочие нюансы

      А инструмента удобного нету, чтобы было минимум действий(под ключ скачал-запустил, и всё там уже настроено как нужно, без докачек и донастроек), постоянно что-то настраивать надо(тоесть всё надо буквально настраивать, ошибиться очень легко)

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

      Линукс там еще бибилиотеки и прочее, да даже в винде хаос настроек


    1. Jijiki
      09.02.2026 14:13

      а что будет если вы оставите эту строку вам нужна скорость вы используете json stb sdl2 gl, и нечайно или по незнанию включите ffast-math?

      тогда нстройка инструмента превратится в квест, нам нужна скорость и поддержка проверки infinity(строка в джейсоне 10832 - поидее эту проверку менять нельзя, значит надо сделать так чтоб у джейсона была проверка, что это как не то самое в виде настроек) и nan,

      как следствие

      Скрытый текст
      cmake_minimum_required(VERSION 3.25)
      project(EngineRPG LANGUAGES CXX)
      
      # С++20 - золотая середина (стабильно и мощно) или 26
      set(CMAKE_CXX_STANDARD 23)
      set(CMAKE_CXX_STANDARD_REQUIRED ON)
      
      # ПРИНУДИТЕЛЬНАЯ СТАТИКА (для vcpkg или системных либ)
      set(BUILD_SHARED_LIBS OFF)
      set(VCPKG_TARGET_TRIPLET x64-linux-static CACHE STRING "") # или x64-windows-static
      
      # Поиск библиотек
      find_package(SDL2 REQUIRED)
      find_package(GLEW REQUIRED)
      find_package(OpenGL REQUIRED)
      
      # Флаги "Брони"
      if(NOT MSVC)
          add_compile_options(
              -Wall -Wextra -Wpedantic -Wshadow -Wconversion
              -fno-fast-math              # Лечим бесконечность в JSON
              -fstack-protector-strong    # Защита от взлома
          )
      endif()
      
      # СОБИРАЕМ ПРОЕКТ
      # Добавляем файлы реализации JSON и STB сюда!
      add_executable(EngineRPG
          main.cpp
          json_wrapper.cpp
          stb_impl.cpp
          # Persistency.cpp и другие...
      )
      
      # Линк
      target_link_libraries(EngineRPG PRIVATE
          GLEW::GLEW
          OpenGL::GL
          SDL2::SDL2
      )
      
      # Помечаем сторонние папки как SYSTEM (Игнорируем их мусор)
      target_include_directories(EngineRPG SYSTEM PRIVATE
          ${SDL2_INCLUDE_DIRS}
          ${GLEW_INCLUDE_DIRS}
                      # Где лежит stb
          .          # Где лежит json
      )
      
      # Включаем LTO для максимального сжатия статики в один бинарник
      include(CheckIPOSupported)
      check_ipo_supported(RESULT lto_supported)
      if(lto_supported)
          set_target_properties(EngineRPG PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE)
      endif()
      

      что-то около такого файла, и из-за отсутствия агрессивной оптимизации что-то теряем(например скорость), тоесть квест усложняется буквально на каждом желании сохраняя скорость оставить и инфинити для джейсона и память, и скорость самого приложения

      а мы же еще можем посмотреть в сторону vcpkg или Conan(можно еще докер поставить настроить вообще всё ради изоляции и безопасного отлова ошибок!) и квест станет еще гуще, и тогда переход на Раст это просто дело времени!


  1. ImagineTables
    09.02.2026 14:13

    Автор: LLM, раздуй «Йода-сравнение» до длинного и круто выглядящего текста.

    LLM:

    Вместо if ($q = 2) мы пишем условие в инвертированном виде: if (2 = $q), и тем самым создаем логический парадокс машинного кода

    Автор: Да нет, раздуй по-настоящему.

    LLM:


  1. XViivi
    09.02.2026 14:13

    в расте это как будто окончательно решили тем, что запретили = возвращать что либо кроме (). как по мне, само то, что присваивание возвращает ссылку является стрельбой в ногу, как и возможность типам быть конвертируемыми в bool кроме пары случаев когда тип действительно близок по смыслу навроде bool? в C# ну или, может быть, любых option-типов


    1. Jijiki
      09.02.2026 14:13

      следующий на очереди бул, потомучто проще u8 же, после раста я понял что это тип чисто с нюансами, он не весит 1 бит как я понимаю, это критически важно походу дела


  1. AHL
    09.02.2026 14:13

    Старый добрый Borland C++ 5 02 в таких случаях обязательно выдаёт предупреждение. В чём проблема то?


    1. Jijiki
      09.02.2026 14:13

      проблема как я считаю в общем, так-то конечно всё удобно,

      Скрытый текст
      clang++ -std=c++26 -O3 -ffast-math -msse4.2 -mavx2 main.cpp imgui/imgui.cpp imgui/imgui_draw.cpp imgui/imgui_widgets.cpp imgui/imgui_tables.cpp imgui/imgui_impl_sdl2.cpp imgui/imgui_impl_opengl3.cpp -o rgp_engine -I/usr/include/SDL2/ -lSDL2 -lGLEW -lGL
      In file included from main.cpp:4:
      In file included from ./Persistency.hpp:2:
      ./json.hpp:10832:31: warning: use of infinity is undefined behavior due to the currently enabled floating-point options [-Wnan-infinity-disabled]
       10832 |                             ? std::numeric_limits<double>::infinity()
             |                               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      ./json.hpp:12397:31: warning: use of infinity is undefined behavior due to the currently enabled floating-point options [-Wnan-infinity-disabled]
       12397 |                             ? std::numeric_limits<double>::infinity()
             |                               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      ./json.hpp:13321:51: warning: use of infinity is undefined behavior due to the currently enabled floating-point options [-Wnan-infinity-disabled]
       13321 |                         if (JSON_HEDLEY_UNLIKELY(!std::isfinite(res)))
             |                                                   ^~~~~~~~~~~~~~~~~~
      ./json.hpp:1689:94: note: expanded from macro 'JSON_HEDLEY_UNLIKELY'
       1689 | #  define JSON_HEDLEY_UNLIKELY(expr)                    __builtin_expect                 (!!(expr),    0                  )
            |                                                                                              ^~~~
      ./json.hpp:13151:13: note: in instantiation of function template specialization 'nlohmann::detail::parser<nlohmann::basic_json<>, nlohmann::detail::iterator_input_adapter<const char
            *>>::sax_parse_internal<nlohmann::detail::json_sax_dom_callback_parser<nlohmann::basic_json<>, nlohmann::detail::iterator_input_adapter<const char *>>>' requested here
       13151 |             sax_parse_internal(&sdp);
             |             ^
      ./json.hpp:24306:148: note: in instantiation of member function 'nlohmann::detail::parser<nlohmann::basic_json<>, nlohmann::detail::iterator_input_adapter<const char *>>::parse' requested here
       24306 |         parser(detail::input_adapter(std::move(first), std::move(last)), std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas).parse(true, result); // cppcheck-suppress[accessMoved]
             |                                                                                                                                                    ^
      ./json.hpp:25520:28: note: in instantiation of function template specialization 'nlohmann::basic_json<>::parse<const char *>' requested here
       25520 |     return nlohmann::json::parse(s, s + n);
             |                            ^
      ./json.hpp:13321:51: warning: use of infinity is undefined behavior due to the currently enabled floating-point options [-Wnan-infinity-disabled]
       13321 |                         if (JSON_HEDLEY_UNLIKELY(!std::isfinite(res)))
             |                                                   ^~~~~~~~~~~~~~~~~~
      ./json.hpp:1689:94: note: expanded from macro 'JSON_HEDLEY_UNLIKELY'
       1689 | #  define JSON_HEDLEY_UNLIKELY(expr)                    __builtin_expect                 (!!(expr),    0                  )
            |                                                                                              ^~~~
      ./json.hpp:13179:13: note: in instantiation of function template specialization 'nlohmann::detail::parser<nlohmann::basic_json<>, nlohmann::detail::iterator_input_adapter<const char
            *>>::sax_parse_internal<nlohmann::detail::json_sax_dom_parser<nlohmann::basic_json<>, nlohmann::detail::iterator_input_adapter<const char *>>>' requested here
       13179 |             sax_parse_internal(&sdp);
             |             ^
      ./json.hpp:24306:148: note: in instantiation of member function 'nlohmann::detail::parser<nlohmann::basic_json<>, nlohmann::detail::iterator_input_adapter<const char *>>::parse' requested here
       24306 |         parser(detail::input_adapter(std::move(first), std::move(last)), std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas).parse(true, result); // cppcheck-suppress[accessMoved]
             |                                                                                                                                                    ^
      ./json.hpp:25520:28: note: in instantiation of function template specialization 'nlohmann::basic_json<>::parse<const char *>' requested here
       25520 |     return nlohmann::json::parse(s, s + n);
             |                            ^
      ./json.hpp:19746:14: warning: use of infinity is undefined behavior due to the currently enabled floating-point options [-Wnan-infinity-disabled]
       19746 |         if (!std::isfinite(x))
             |              ^~~~~~~~~~~~~~~~
      ./json.hpp:19299:17: note: in instantiation of member function 'nlohmann::detail::serializer<nlohmann::basic_json<>>::dump_float' requested here
       19299 |                 dump_float(val.m_data.m_value.number_float);
             |                 ^
      ./json.hpp:21547:15: note: in instantiation of member function 'nlohmann::detail::serializer<nlohmann::basic_json<>>::dump' requested here
       21547 |             s.dump(*this, true, ensure_ascii, static_cast<unsigned int>(indent));
             |               ^
      ./Persistency.hpp:42:18: note: in instantiation of member function 'nlohmann::basic_json<>::dump' requested here
         42 |     file << data.dump(4); // Красивый отступ
            |                  ^
      In file included from main.cpp:4:
      In file included from ./Persistency.hpp:2:
      ./json.hpp:18911:17: warning: use of infinity is undefined behavior due to the currently enabled floating-point options [-Wnan-infinity-disabled]
       18911 |     JSON_ASSERT(std::isfinite(value));
             |                 ^~~~~~~~~~~~~~~~~~~~
      ./json.hpp:2575:35: note: expanded from macro 'JSON_ASSERT'
       2575 |     #define JSON_ASSERT(x) assert(x)
            |                                   ^
      /usr/include/assert.h:100:27: note: expanded from macro 'assert'
        100 |      (static_cast <bool> (expr)                                         \
            |                           ^~~~
      ./json.hpp:19767:41: note: in instantiation of function template specialization 'nlohmann::detail::to_chars<double>' requested here
       19767 |         auto* end = ::nlohmann::detail::to_chars(begin, begin + number_buffer.size(), x);
             |                                         ^
      ./json.hpp:19761:9: note: in instantiation of member function 'nlohmann::detail::serializer<nlohmann::basic_json<>>::dump_float' requested here
       19761 |         dump_float(x, std::integral_constant<bool, is_ieee_single_or_double>());
             |         ^
      ./json.hpp:19299:17: note: in instantiation of member function 'nlohmann::detail::serializer<nlohmann::basic_json<>>::dump_float' requested here
       19299 |                 dump_float(val.m_data.m_value.number_float);
             |                 ^
      ./json.hpp:21547:15: note: in instantiation of member function 'nlohmann::detail::serializer<nlohmann::basic_json<>>::dump' requested here
       21547 |             s.dump(*this, true, ensure_ascii, static_cast<unsigned int>(indent));
             |               ^
      ./Persistency.hpp:42:18: note: in instantiation of member function 'nlohmann::basic_json<>::dump' requested here
         42 |     file << data.dump(4); // Красивый отступ
            |                  ^
      In file included from main.cpp:4:
      In file included from ./Persistency.hpp:2:
      ./json.hpp:18736:17: warning: use of infinity is undefined behavior due to the currently enabled floating-point options [-Wnan-infinity-disabled]
       18736 |     JSON_ASSERT(std::isfinite(value));
             |                 ^~~~~~~~~~~~~~~~~~~~
      ./json.hpp:2575:35: note: expanded from macro 'JSON_ASSERT'
       2575 |     #define JSON_ASSERT(x) assert(x)
            |                                   ^
      /usr/include/assert.h:100:27: note: expanded from macro 'assert'
        100 |      (static_cast <bool> (expr)                                         \
            |                           ^~~~
      ./json.hpp:18944:16: note: in instantiation of function template specialization 'nlohmann::detail::dtoa_impl::grisu2<double>' requested here
       18944 |     dtoa_impl::grisu2(first, len, decimal_exponent, value);
             |                ^
      ./json.hpp:19767:41: note: in instantiation of function template specialization 'nlohmann::detail::to_chars<double>' requested here
       19767 |         auto* end = ::nlohmann::detail::to_chars(begin, begin + number_buffer.size(), x);
             |                                         ^
      ./json.hpp:19761:9: note: in instantiation of member function 'nlohmann::detail::serializer<nlohmann::basic_json<>>::dump_float' requested here
       19761 |         dump_float(x, std::integral_constant<bool, is_ieee_single_or_double>());
             |         ^
      ./json.hpp:19299:17: note: in instantiation of member function 'nlohmann::detail::serializer<nlohmann::basic_json<>>::dump_float' requested here
       19299 |                 dump_float(val.m_data.m_value.number_float);
             |                 ^
      ./json.hpp:21547:15: note: in instantiation of member function 'nlohmann::detail::serializer<nlohmann::basic_json<>>::dump' requested here
       21547 |             s.dump(*this, true, ensure_ascii, static_cast<unsigned int>(indent));
             |               ^
      ./Persistency.hpp:42:18: note: in instantiation of member function 'nlohmann::basic_json<>::dump' requested here
         42 |     file << data.dump(4); // Красивый отступ
            |                  ^
      In file included from main.cpp:4:
      In file included from ./Persistency.hpp:2:
      ./json.hpp:18034:17: warning: use of infinity is undefined behavior due to the currently enabled floating-point options [-Wnan-infinity-disabled]
       18034 |     JSON_ASSERT(std::isfinite(value));
             |                 ^~~~~~~~~~~~~~~~~~~~
      ./json.hpp:2575:35: note: expanded from macro 'JSON_ASSERT'
       2575 |     #define JSON_ASSERT(x) assert(x)
            |                                   ^
      /usr/include/assert.h:100:27: note: expanded from macro 'assert'
        100 |      (static_cast <bool> (expr)                                         \
            |                           ^~~~
      ./json.hpp:18758:26: note: in instantiation of function template specialization 'nlohmann::detail::dtoa_impl::compute_boundaries<double>' requested here
       18758 |     const boundaries w = compute_boundaries(value);
             |                          ^
      ./json.hpp:18944:16: note: in instantiation of function template specialization 'nlohmann::detail::dtoa_impl::grisu2<double>' requested here
       18944 |     dtoa_impl::grisu2(first, len, decimal_exponent, value);
             |                ^
      ./json.hpp:19767:41: note: in instantiation of function template specialization 'nlohmann::detail::to_chars<double>' requested here
       19767 |         auto* end = ::nlohmann::detail::to_chars(begin, begin + number_buffer.size(), x);
             |                                         ^
      ./json.hpp:19761:9: note: in instantiation of member function 'nlohmann::detail::serializer<nlohmann::basic_json<>>::dump_float' requested here
       19761 |         dump_float(x, std::integral_constant<bool, is_ieee_single_or_double>());
             |         ^
      ./json.hpp:19299:17: note: in instantiation of member function 'nlohmann::detail::serializer<nlohmann::basic_json<>>::dump_float' requested here
       19299 |                 dump_float(val.m_data.m_value.number_float);
             |                 ^
      ./json.hpp:21547:15: note: in instantiation of member function 'nlohmann::detail::serializer<nlohmann::basic_json<>>::dump' requested here
       21547 |             s.dump(*this, true, ensure_ascii, static_cast<unsigned int>(indent));
             |               ^
      ./Persistency.hpp:42:18: note: in instantiation of member function 'nlohmann::basic_json<>::dump' requested here
         42 |     file << data.dump(4); // Красивый отступ
            |                  ^
      In file included from main.cpp:4:
      In file included from ./Persistency.hpp:2:
      ./json.hpp:13321:51: warning: use of infinity is undefined behavior due to the currently enabled floating-point options [-Wnan-infinity-disabled]
       13321 |                         if (JSON_HEDLEY_UNLIKELY(!std::isfinite(res)))
             |                                                   ^~~~~~~~~~~~~~~~~~
      ./json.hpp:1689:94: note: expanded from macro 'JSON_HEDLEY_UNLIKELY'
       1689 | #  define JSON_HEDLEY_UNLIKELY(expr)                    __builtin_expect                 (!!(expr),    0                  )
            |                                                                                              ^~~~
      ./json.hpp:13151:13: note: in instantiation of function template specialization 'nlohmann::detail::parser<nlohmann::basic_json<>,
            nlohmann::detail::input_stream_adapter>::sax_parse_internal<nlohmann::detail::json_sax_dom_callback_parser<nlohmann::basic_json<>, nlohmann::detail::input_stream_adapter>>' requested here
       13151 |             sax_parse_internal(&sdp);
             |             ^
      ./json.hpp:24470:42: note: in instantiation of member function 'nlohmann::detail::parser<nlohmann::basic_json<>, nlohmann::detail::input_stream_adapter>::parse' requested here
       24470 |         parser(detail::input_adapter(i)).parse(false, j);
             |                                          ^
      ./Persistency.hpp:51:10: note: in instantiation of member function 'nlohmann::operator>>' requested here
         51 |     file >> data;
            |          ^
      In file included from main.cpp:4:
      In file included from ./Persistency.hpp:2:
      ./json.hpp:13321:51: warning: use of infinity is undefined behavior due to the currently enabled floating-point options [-Wnan-infinity-disabled]
       13321 |                         if (JSON_HEDLEY_UNLIKELY(!std::isfinite(res)))
             |                                                   ^~~~~~~~~~~~~~~~~~
      ./json.hpp:1689:94: note: expanded from macro 'JSON_HEDLEY_UNLIKELY'
       1689 | #  define JSON_HEDLEY_UNLIKELY(expr)                    __builtin_expect                 (!!(expr),    0                  )
            |                                                                                              ^~~~
      ./json.hpp:13179:13: note: in instantiation of function template specialization 'nlohmann::detail::parser<nlohmann::basic_json<>,
            nlohmann::detail::input_stream_adapter>::sax_parse_internal<nlohmann::detail::json_sax_dom_parser<nlohmann::basic_json<>, nlohmann::detail::input_stream_adapter>>' requested here
       13179 |             sax_parse_internal(&sdp);
             |             ^
      ./json.hpp:24470:42: note: in instantiation of member function 'nlohmann::detail::parser<nlohmann::basic_json<>, nlohmann::detail::input_stream_adapter>::parse' requested here
       24470 |         parser(detail::input_adapter(i)).parse(false, j);
             |                                          ^
      ./Persistency.hpp:51:10: note: in instantiation of member function 'nlohmann::operator>>' requested here
         51 |     file >> data;
            |          ^
      10 warnings generated.
      

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