Изучение синтаксиса Rust с двумя Помидорами

Rust Примечания
Изучение синтаксиса Rust с двумя Помидорами

Часть примера кода в этой статье находится вyoungjuning/learn-rust, статья была впервые опубликована вОфициальный сайт Луожу

Языки имеют схожие основные функции, такие как такие понятия, как переменные, примитивные типы, функции, аннотации и поток управления. Но у каждого языка есть свои уникальные концепции, и эта статья предназначена для записи уникальных концепций Rust, с которыми я столкнулся в процессе изучения синтаксиса Rust.

Переменные и изменчивость

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

// main.rs
fn main() {
  let x = 5;
  println!("The value of x is: {}", x);
  x = 6;
  println!("The value of x is: {}", x)
}

x = 6Это относится к повторяющемуся присваиванию, поэтому выполнитеcargo runКомпиляция не пройдет:

Мы можем сделать это, добавив перед объявленным именем переменнойmutключевое слово, чтобы сделать его изменяемым. В дополнение к тому, чтобы сделать значение переменной изменяемым,mutЭто также намекает тому, кто читает код, что другой код может изменить значение этой переменной.

Разница между константами и переменными

  1. мы не можем использоватьmutключевое слово для изменения переменной. Константа не только неизменяема по умолчанию, она неизменна всегда.
  2. вам нужно использоватьconstключевое слово вместоletключевое слово для объявления константы.
  3. Во время объявления вы должны явно аннотировать тип значения.
  4. Константы могут быть объявлены в любой области видимости, даже глобальной. Это полезно, когда на значение нужно ссылаться в разных частях кода.
  5. Вы можете привязать константу только к константному выражению, вы не можете привязать возвращаемое значение функции или другое значение, которое необходимо вычислить во время выполнения, к константе.

Мы договорились и в просторечии используем заглавные буквы с разделителями подчеркивания для имени константы и вставляем символы подчеркивания в значения для улучшения читабельности:const MAX_POINTS: u32 = 100_000;

Спрятать

В Rust вновь объявленная переменная может перезаписать старую переменную с таким же именем Мы описываем это явление так: первая переменная затеняется второй переменной. Это означает, что когда мы будем использовать это имя позже, оно будет ссылаться на вторую переменную. мы можем повторно использоватьletключевое слово и присвоение того же имени, чтобы скрыть переменные:

fn main() {
    let x = 5;
    let x = x + 1;
    let x = x * 2;
    println!("The value of x is: {}", x)
}

Как показано на рисунке ниже, мы видим, что эта функция не существует в JavaScript:

Механизмы сокрытия и переменные, объявленные какmutразница:

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

тип данных

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

Скалярный тип (скалярный)

скалярТип — это общий термин для одного типа значения. В Rust встроено четыре основных скалярных типа: целые числа, числа с плавающей запятой, логические значения и символы. Это похоже на примитивные типы в JavaScript.

Целочисленный тип

Целые числа — это числа без дробной части.

длина подписал неподписанный
8 bit i8 (от -128 до 127) U8 (от 0 до 255)
16 bit i16 (от -32768 до 32767) u16 (от 0 до 65535)
32 bit i32 (по умолчанию) u32
64 bit i64 u64
arch isize usize

Isize и usize — это специальные целочисленные типы, длина которых зависит от целевой платформы, на которой работает программа. В 64-битной архитектуре они 64-битные, а в 32-битной архитектуре — 32-битные.

можно использовать_в качестве разделителя для облегчения чтения, например.1_000

как выбрать:

  • Если вы сомневаетесь, выводимый по умолчанию тип Rust i32 для целочисленных литералов обычно является хорошим выбором: в большинстве случаев это самая быстрая операция.
  • Еще два специальных целочисленных типаusizeиisizeв основном используется как индекс для некоторых коллекций

тип с плавающей запятой

  • числа одинарной точности с плавающей запятой (f32)
  • Числа двойной точности с плавающей запятой (f64)(дефолт)

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

fn main() {
  let x = 2.0; // f64

  let y: f32 = 3.0; // f32
}

Логическое значение

Как и в Go, логический тип Rust имеет только два возможных значения:trueиfalse. по сравнению с Javascriptfalse,0,NaN,'',null,undefined6 можно превратить вfalseЭто действительно экономит силы мозга.

fn main() {
  let t = true;
  let f: bool = false // 附带了显式类型标注的语句
}

тип персонажа

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

составной тип

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

тип кортежа

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

Создайте кортеж:

fn main() {
    let tup: (i32, f64, u8) = (500, 6.4, 1);
}
  • поместите некоторые значения столбца, разделенные запятыми, в пару круглых скобок
  • Значение в каждой позиции кортежа имеет тип, и типы не обязательно должны быть одинаковыми.

Вышеприведенный абзац приводит к исполнениюcargo runБудет предупреждение о компиляции:

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

Ценность:

1. Деструктуризация: используйте сопоставление с образцом для деконструкции кортежей:

fn main() {
    let tup = (500, 6.4, 1);
    let (_x, y, _z) = tup;
    println!("The value of y is: {}", y);
}

2. По индексу и с помощью точек (.) для доступа к значениям в кортеже:

fn main() {
  let x: (i32, f64, u8) = (500, 6.4, 1);
  let five_hundred = x.0;
  let six_point_four = x.1;
  let one = x.2;
}

тип массива

массив (массив)

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

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

В Rust вы можете создать массив, поместив значения через запятую в квадратные скобки:

fn main() {
  let a = [1, 2, 3, 4, 5]
}

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

let a: [i32, 5] = [1, 2, 3, 4, 5]

Создайте массив с такими же элементами:

let a = [3; 5]

отaИменованный массив будет иметь 5 элементов, все из которых имеют одинаковое начальное значение 3. Это письмо эквивалентноlet a = [3, 3, 3, 3, 3].

Доступ к элементам массива:

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

fn main() {
	let a = [1, 2, 3, 4, 5];
  let first = a[0];
  let second = a[1];
}

неправильный доступ к массиву

fn main() {
  let a = [1, 2, 3, 4, 5];
  let index = 10;

  let element = a[index];
  println!("The value of elements is: {}", element)
}

существуетRust PlaygroundЗапустив этот код, компилятор сообщает нам, что эта операция завершится сбоем во время выполнения. Причина в том, что индекс выходит за пределы (распространенная ошибка Java), длина массива равна 5, а индекс, который мы дали, равен 10.

Многие низкоуровневые языки не имеют подобных проверок, и если вы попытаетесь использовать недопустимый индекс, вы получите доступ к недопустимому блоку памяти (в JavaScript он возвращаетundefined)

Динамический массив (вектор)

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

функция

  • mainФункции — это то, с чего начинается большинство программ.
  • использоватьfnключевое слово для объявления новой функции.
  • Код Rust использует змеиный регистр в качестве стиля для нормализации имен функций и переменных. В именах змей используются только строчные буквы, а слова разделяются символами подчеркивания.
fn main () {
    println!("Hello world!");

    another_function();
}

fn another_function() {
    println!("Another function");
}

параметр функции

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

fn main() {
  another_function(5);
}

fn another_function(x: i32) {
  println!("The value of x is: {}", x)
}

Как и в Go, в сигнатурах функций Rust вы должны явно объявлять тип каждого параметра.

Как и другие языки программирования, Rust использует,для разделения нескольких параметров:

fn main() {
  another_function(5, 6);
}

fn another_function(x: i32, y: i32) {
  println!("The value of x is: {}", x);
  println!("The value of y is: {}", y);
}

Операторы и выражения в телах функций

Поскольку Rust — это язык, основанный на выражениях, он отличает оператор от выражения как два разных понятия. Операторы — это те инструкции, которые выполняют операцию, но не возвращают значение, а выражения — это те, которые выполняют вычисления и в результате получают значение. Это не то же самое для других языков:

В C, Ruby, JavaScriptvar x = y = 6Этот оператор присваивания возвращает присвоенное значение, но он не работает в Rust.

Стоит отметить, что в следующем кодеx+1это выражение.

fn main() {
  let x = 5;

  let y = {
    let x = 3;

    x + 1
  }

  println!("The value of y is: {}", y);
}

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

возвращаемое значение функции

  • В Rust возвращаемое значение функции равно значению последнего выражения в теле функции.
  • В отличие от других языков программирования, функции в Rustreturnоператор не требуется, просто ключевое слово, используемое для раннего возврата. И большинство функций неявно возвращают финальное выражение.
  • нужно быть в тощей стрелке(->) после объявления его типа.

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

fn main() {
  let x = plus_one(5);
  
  println!("The value of x is: {}", x);
}

fn plus_one(x: i32) -> i32 {
  x + 1;
}

Попытка скомпилировать этот код приводит к следующему сообщению об ошибке:

Видно, что из-заx +1;состоит в том, что выражение не имеет возвращаемого значения, а тело функции неявно возвращает пустой кортеж (()), что, в свою очередь, вызывает время компиляцииmismatched typesОшибка. Совет, данный компилятором, состоит в том, чтобы удалитьx +1;Точка с запятой в заявлении.

поток управления

если выражение

fn main() {
    let number = 3;

    if number < 5 {
        println!("condition was true");
    } else {
        println!("condition was false")
    }
}
  • Как и в Go, условия не нуждаются в скобках.
  • Условные выражения в коде должны создаватьboolзначение типа, иначе будет вызвана ошибка компиляции.
  • В отличие от таких языков, как Ruby или JavaScript, в Rust нет неявных преобразований.

существуетletиспользуется в заявленииif

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

fn main() {
  let condition = true;
  let number = if condition {
    5
  } else {
    6
  };

  println!("The value of number is: {}", number);
}
  • Значение, выдаваемое кодовым блоком, является значением последнего выражения в нем, а само число также может использоваться как выражение.
  • весьifЗначение выражения зависит от того, какой блок кода был выполнен. Это означает, что всеifВсе возможные значения, возвращаемые веткой, должны быть одного типа.

Используйте циклы для многократного выполнения кода

Rust предоставляет 3 вида циклов:loop,whileиfor

петля петля

fn main() {
  loop {
    println!("again!")
  }
}

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

возвращаемое значение из цикла

fn main() {
  let mut counter = 0;
  
  let result = loop {
    counter += 1;
    
    if counter == 10 {
      break counter * 2
    }
  };
  
  println!("The result is {}", result);
}

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

пока условный цикл

fn main() {
  let mut number = 3;
  
  while number !=0 {
    println!("{}!", number);
    
    number = number - 1;
  }
  
  println!("LOFTOFF!!!")
}

whileРежим цикла заключается в оценке условия перед каждым выполнением тела цикла.Если условие истинно, выполняется фрагмент кода, а если условие ложно или встречается во время выполненияbreakдля выхода из текущего цикла. Этот режим может бытьloop,if,elseиbreakДля этого используется комбинация ключевых слов.

Перебор коллекции с помощью цикла for

мы можем использоватьforЦикл для перебора каждого элемента в коллекции.

fn main() {
  let a = [10, 20, 30, 40, 50];
  
  for element in a.iter() {
    println!("The value is: {}", element)
  }
}

forБезопасность и простота циклов делают циклы наиболее часто используемой конструкцией цикла в Rust. Большинство разработчиков Rust также предпочитают использовать циклы for.

В следующем примере мы взаимодействуем с Range в стандартной библиотеке для печати1прибыть4:

fn main() {
  for number in (1..4).rev() {
    println!("{}!", number);
  }
  println!("LIFTOFF!!!")
}

История не окончена

После поиска я не нашел группу по обмену Rust, поэтому я создал ее сам.

У Tuya Smart большое количество качественных НС, присоединяться могут все желающие, нужно добавить меня в WeChat yang_jun_ning, или прислать свое резюме прямо на почтуyoungjuning@aliyun.com