Форум программистов, компьютерный форум, киберфорум
Rust
Войти
Регистрация
Восстановить пароль
 
 
Рейтинг 4.50/6: Рейтинг темы: голосов - 6, средняя оценка - 4.50
Заблокирован
1

Rust не хватает функциональности?

20.08.2020, 17:24. Показов 1222. Ответов 29
Метки нет (Все метки)

Rust вобрал в себя немало из функциональных языков, но ему, по-моему мнению, все же функциональности не достает.
Простой пример: вы почему-то захотели написать свою функцию swap, которая меняет местами два ближайших элемента в векторе, а не воспользоваться уже существующей. Возможно вы не знали про ее существование или вам нужен немного другой функционал. Поскольку язык императивный, то вы решили сначала попробовать поменять местами элементы с помощью обмена через третью переменную. Но тут работать это не будет, так как нельзя одновременно заимствовать ссылку на объект и изменят этот же объект.
Как человек который знает как хорошо дело обстоит с алгоритмами в языке Lisp, как это там удобно благодаря широкому набору необходимых функций.
Начнем смотреть - есть метод first(), который возвращает ссылку на первый элемент вектора; есть метод pop() который возвращает последний элемент вектора и удалет его же из этого вектора; есть метод last() - в целом неплохо, но не хватает методов вроде second(), rest() и прочих, которые бы сделали жизнь намного проще. Интересно почему создатели забили на них и не определили их для этой коллекции и прочих коллекций?

Как пример
C++ (Qt)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
fn second(v: &[i32]) -> Option<i32> {
  if v.len() >= 2 {
     let r = v[1];
     Some(r)
  } else {
     None
 }
}
 
fn rest(v: Vec<i32>, num: usize) -> Option<Vec<i32>> {
    if v.len() > num  {
     let new_vec = v[num..].iter().map(|x| *x).collect::<Vec<i32>>();
     Some(new_vec)
    } else {
       None
  }
    
 }
       
fn main() {
  let mut vector = vec![1,2,3,4,5,6,7,8,9];
  let mut new_vector = vec![]; 
   for _i in 0..v.len() {
     if v.len() >= 2 {
       new_vector.push(second(&vector).unwrap());
       new_vector.push(*(vector.first().unwrap()));
       vector = rest(vector, 2).unwrap();
   }
  }
 
 for i in &new_vector {
    println!("{}", i);
 }
}
Поменяли местами элементы.
1
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
20.08.2020, 17:24
Ответы с готовыми решениями:

[Rust] Обсуждение возможностей и предстоящей роли языка Rust
Psilon, чем он тебя так привлек? И почему именно &quot;убийца плюсов&quot;? Если напишешь развернутый ответ,...

[Rust] Как привязывать WinAPI-функции к коду на Rust?
Может кто-нить дать код, КАК привязывать вин апишные функции к растовскому коду (на примере...

[Rust] Расскажите о своём опыте программирования на Rust
Доброе утро! Расскажите, пожалуйста, о своём опыте программирования на Rust. Можно в сравнении с...

Выводится сообщение о том, что не хватает памяти, хотя памяти хватает
Файлы подкачки отключил и включать не собираюсь. Когда свободной памяти остаётся примерно...

__________________
29
4135 / 2713 / 388
Регистрация: 01.06.2013
Сообщений: 5,713
Записей в блоге: 9
21.08.2020, 01:01 2
Цитата Сообщение от sodda Посмотреть сообщение
вы решили сначала попробовать поменять местами элементы с помощью обмена через третью переменную. Но тут работать это не будет
Отчего же. Пример без использования unsafe, в отличии от std::mem::swap.
C++ (Qt)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
pub fn copy_swap<T>(x: &mut T, y: &mut T) 
    where T : Copy
{
    let tmp = *x;
    *x=*y;
    *y=tmp;
}
 
pub fn clone_swap<T>(x: &mut T, y: &mut T) 
    where T : Clone
{
    let tmp = x.clone();
    *x=y.clone();
    *y=tmp;
}
 
fn main() {
    let mut i1 = 11;
    let mut i2 = 22;
    copy_swap(&mut i1,&mut i2);
    println!("{}   -    {}", i1,i2);
 
    let mut s1 = "qwerty".to_string();
    let mut s2 = "asdfgh".to_string();
    clone_swap(&mut s1,&mut s2);
    println!("{}   -    {}", s1,s2);
}
Может быть copy_swap и clone_swap можно как ни будь "перегрузить" через трейт, что бы использовать одно имя swap в обоих случаях, но я не знаю как.
0
Заблокирован
21.08.2020, 12:01  [ТС] 3
Цитата Сообщение от Curry Посмотреть сообщение
Отчего же. Пример без использования unsafe, в отличии от std::mem::swap.
Массив в цикле так обойти не выйдет, наверно.
Цитата Сообщение от Curry Посмотреть сообщение
Может быть copy_swap и clone_swap можно как ни будь "перегрузить" через трейт, что бы использовать одно имя swap в обоих случаях, но я не знаю как.
Конечно можно. Для Clone сами допишите, я думаю)

C++ (Qt)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
trait Swap<T> {
  fn swap(x: &mut T , y: &mut T);
 
}
 
impl<T: Copy> Swap<T> for i32 {
    fn swap(x: &mut T, y: &mut T)  {
        let tmp = *x;
        *x = *y;
        *y=tmp;
 }
}
 
fn main() {
 
let mut x: i32  = 10;
let mut y: i32  = 20;
println!("До применения swap: x = {}\ny = {}", x, y);
i32::swap(&mut x, &mut y);
println!("После применения swap x = {}\ny = {}", x, y);
 
/*
До применения swap: x = 10
y = 20
После применения swap x = 20
y = 10
*/
}
0
4135 / 2713 / 388
Регистрация: 01.06.2013
Сообщений: 5,713
Записей в блоге: 9
21.08.2020, 12:21 4
Цитата Сообщение от sodda Посмотреть сообщение
Конечно можно.
Это вы для конкретного типа i32 сделали. А я имею ввиду для произвольного, и что бы компилятор различал, когда вариант функции для копируемых типов подставлять, когда для клонируемых.
0
Заблокирован
21.08.2020, 13:02  [ТС] 5
Цитата Сообщение от Curry Посмотреть сообщение
Это вы для конкретного типа i32 сделали. А я имею ввиду для произвольного, и что бы компилятор различал, когда вариант функции для копируемых типов подставлять, когда для клонируемых.
Не вижу противоречий.
Наверно вас смущает нотация i32::swap().
Можно реализовать это не как ассоциативную функцию, а как метод где будет нотация такая:
C++ (Qt)
1
2
3
let x  = 10;
let y  = 20;
x.swap(y);
Но тогда нужно было бы T приводить к конкретному типу. В моем случаи к i32.
И вот еще что: чтобы методы характеристики работали, характеристику нужно реализовать для всех типов. Именно так это и работает.
Нужно реализовать характеристику swap для всех копируемых и клонируемых типов и тогда компилятор как раз сам определит где что использовать. Возможно можно просто дополнить характеристики Clone и Copy характеристикой Swap и ее методом. Но суть одно: сами характеристики Clone и Сopy реализованы для каждого типа который должен быть либо оперируемым, либо клонируемым. Именно так работает перегрузка, которая позволяет применять методы и функции с одним названием к разным типам.
Вот простой пример.
У нас есть два типа - Dog и Сat.
Мы хотим использовать один и тот же метод (точнее метод с одним и тем же названием) для обоих типов.

Определяем характеристику с этим методом

C++ (Qt)
1
2
3
trait Pet {
  fn says(&self);
}
Определяем наши типы.

C++ (Qt)
1
2
3
4
5
6
7
8
9
struct Dog<'a> {
  name: &'a str,
  age:  f32
}
 
struct Cat<'b> {
  name: &'b str,
  age:  f32
}


Реализуем нашу характеристику для этих типов.

C++ (Qt)
1
2
3
4
5
6
7
8
9
10
11
impl<'a> Pet for  Dog<'a> {
   fn says(&self) -> () {
     println!("{} says hello", self.name);
   }
}
 
impl<'b> Pet for  Cat<'b> {
   fn says(&self) -> () {
     println!("{} says hello", self.name);
   }
}

И тогда мы сможем использовать методы с одинаковыми именами для обоих типов.

C++ (Qt)
1
2
3
4
5
6
7
8
fn main() {
  
  let cat = Cat {name: "Barsik", age: 1.0};
  let dog = Dog {name: "Sharik", age: 0.5};
  cat.says();
  dog.says();
 
}
А вы, наверно, больше думаете о универсальной функции. Так как она поймет какой тип копируемый, а кокой клонируемый?
Если это можно как-то проверить, то нет проблем - можно сделать такую универсальную функцию.

Добавлено через 11 минут
Curry, вот что StackOwerflow говорит по этому поводу It is not possible to do this in Rust
0
4135 / 2713 / 388
Регистрация: 01.06.2013
Сообщений: 5,713
Записей в блоге: 9
21.08.2020, 16:44 6
Цитата Сообщение от sodda Посмотреть сообщение
Не вижу противоречий.
Хорошо бы если бы компилятор мог выбирать имплементацию в зависимости он реализованных для типа трейтов
C++ (Qt)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
trait Swap<T> {
    fn swap(x: &mut T , y: &mut T);
   
}
   
impl<T: Copy> Swap<T> for T {
    fn swap(x: &mut T, y: &mut T)  {
        let tmp = *x;
        *x = *y;
        *y=tmp;
    }
}
 
 
impl<T: Clone> Swap<T> for T {
    fn swap(x: &mut T, y: &mut T)  {
        let tmp = x.clone();
        *x=y.clone();
        *y=tmp;
    }
}
Но он не может.
Цитата Сообщение от sodda Посмотреть сообщение
Так как она поймет какой тип копируемый, а кокой клонируемый?
В месте вызова функции из трейта тип аргументов уже определён, то есть,известно, какие трейты они поддерживают.
0
Заблокирован
21.08.2020, 20:39  [ТС] 7
Цитата Сообщение от Curry Посмотреть сообщение
В месте вызова функции из трейта тип аргументов уже определён, то есть,известно, какие трейты они поддерживают.
Это определяется во время компиляции, а во время райн-тайма, наверно, это определить не получится. Тип определяется характеристики определяются во время компиляции иначе это была бы уже динамическая типизация. Вообще говоря, если есть универсальная функция ограниченная характеристикой, то при вызове этой функции для , для каждого типа создается свой экземпляр функции конкретно для этого типа. Это при статической диспетчеризации. А при динамической диспетчеризации вызываются виртуальные функции.

Возьмем ваши функции и попробует слепить из них одну, как бы это могло выгладить.
начнем с того, что Copy b Clone - это противоположные характеристики. Если мы напишем
C++ (Qt)
1
T: Copy + Clone
- это просто наложит более строгие ограничения на тип, который должен одновременно реализовывать как характеристику Copy, так и Сlone. Не знаю, есть ли какой-то синтаксис для выбора одной из характеристик и возможно ли это. Просто пофантазируем как могло бы выгладить.


C++ (Qt)
1
2
3
4
5
6
7
8
9
10
11
12
13
pub fn copy<T,>(x: &mut T, y: &mut T) 
    where T : Copy or Clone {
    
    if x is Copy && y is Copy {
      let tmp = *x;
      *x=*y;
      *y=tmp;
   } else {
       let tmp = x.clone();
       *x=y.clone();
       *y=tmp;
   }
}
Как видите, тут сразу встает несколько вопрос и тип в ветвлении должен определяться в ран-тайме.

Добавлено через 2 минуты
Цитата Сообщение от Curry Посмотреть сообщение
Хорошо бы если бы компилятор мог выбирать имплементацию в зависимости он реализованных для типа трейтов
Цитата Сообщение от Curry Посмотреть сообщение
C++ (Qt)
1
2
3
4
5
6
7
impl<T: Clone> Swap<T> for T {
          fn swap(x: &mut T, y: &mut T) {
             let tmp = x.clone();
             *x=y.clone();
            *y=tmp;
     }
}
}
Ну вот ели вы реализуете этот трейт для всех вам нужных копируемых и клонируемых типов, то он сможет выбыть. Больше никак.
0
4135 / 2713 / 388
Регистрация: 01.06.2013
Сообщений: 5,713
Записей в блоге: 9
21.08.2020, 22:27 8
Цитата Сообщение от sodda Посмотреть сообщение
Это определяется во время компиляции
Да, во время компиляции.
А изобретение динамически типизированного раста мне не интересно.
0
Заблокирован
21.08.2020, 22:32  [ТС] 9
Цитата Сообщение от Curry Посмотреть сообщение
А изобретение динамически типизированного раста мне не интересно.
Соглгасен
0
Заблокирован
22.08.2020, 19:01  [ТС] 10
Curry, ктстати, для всех копируемых типов можно определить разом.

C++ (Qt)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
trait Swap {
  fn swap(&mut self, y: &mut Self);
 
}
 
 
 
impl<T> Swap for T where T: Copy {
    fn swap(&mut self,  y: &mut T)  {
        let x = *self;
        *self = *y;
        *y  = x;
 }
}
 
 
 
fn main() {
 
let mut x = 10.32;
let mut y = 20.024;
let mut a  = 10;
let mut b  = 20;
 
println!("До применения swap: x = {}\ny = {}", x, y);
x.swap(&mut y);
println!("После применения swap x = {}\ny = {}", x, y);
println!("До применения swap: x = {}\ny = {}", a, b);
a.swap(&mut b);
println!("После применения swap x = {}\ny = {}", a, b);
}
 
/*
 
До применения swap: x = 10.32
y = 20.024
После применения swap x = 20.024
y = 10.32
До применения swap: x = 10
y = 20
После применения swap x = 20
y = 10
 
*/
0
4135 / 2713 / 388
Регистрация: 01.06.2013
Сообщений: 5,713
Записей в блоге: 9
22.08.2020, 22:43 11
Цитата Сообщение от sodda Посмотреть сообщение
для всех копируемых типов можно определить разом.
Да, да. А теперь попробуйте одновременно добавить и для клонируемых.
0
Заблокирован
22.08.2020, 22:51  [ТС] 12
Цитата Сообщение от Curry Посмотреть сообщение
Да, да. А теперь попробуйте одновременно добавить и для клонируемых.
Пробовал: выдает ошибку. Нужно как-то объединить через третий трейт возможно.
0
4135 / 2713 / 388
Регистрация: 01.06.2013
Сообщений: 5,713
Записей в блоге: 9
22.08.2020, 22:53 13
Цитата Сообщение от sodda Посмотреть сообщение
Нужно как-то объединить через третий трейт возможно.
Вряд ли.
0
Заблокирован
22.08.2020, 23:04  [ТС] 14
Цитата Сообщение от Curry Посмотреть сообщение
Вряд ли.
Короче без 100 грамм не разберешься)
Трейты - это самая сложная штука в языке, а не владение и заимствование.
0
4135 / 2713 / 388
Регистрация: 01.06.2013
Сообщений: 5,713
Записей в блоге: 9
22.08.2020, 23:20 15
Цитата Сообщение от sodda Посмотреть сообщение
Трейты - это самая сложная штука в языке
Мне, после haskell, они сложными не кажутся. Там они называются классы типов (type classes).
0
Заблокирован
22.08.2020, 23:47  [ТС] 16
Цитата Сообщение от Curry Посмотреть сообщение
Мне, после haskell, они сложными не кажутся. Там они называются классы типов (type classes).
Они оттуда и взяты.
В самих нет ничего сложного, но в их перипетиях порой сложно разобраться.

В общем только вручную определять похоже, как я и говорил выше.
Так работает.
C++ (Qt)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
trait Swap {
  fn swap(&mut self, y: &mut Self);
}
 
 
impl Swap for i32 {
  fn swap(&mut self, y: &mut Self) {
    let tmp = *self;
    *self = *y;
    *y = tmp;
  }
}
 
impl Swap for String {
  fn swap(&mut self, y: &mut Self) {
    let tmp = self.clone();
    *self = y.clone();
    *y = tmp;
  }
}
 
fn main() {
  let mut a = 10;
  let mut b = 20;
  let mut x = "Mir".to_string();
  let mut y = "May".to_string();
  a.swap(&mut b);
  println!("a = {}, b =  {}", a, b);
  x.swap(&mut y);
  println!("mir = {}, may =  {}", x, y);
}
 
/*
 
a = 20, b =  10
mir = May, may =  Mir
 
*/
Не даром, наверно, если заглянуть в библиотеку, то можно увидеть что все арифметические действия имплементируются с помощью макроса.
C++ (Qt)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#[doc(alias = "+")]
pub trait Add<Rhs = Self> {
    /// The resulting type after applying the `+` operator.
    #[stable(feature = "rust1", since = "1.0.0")]
    type Output;
 
    /// Performs the `+` operation.
    #[must_use]
    #[stable(feature = "rust1", since = "1.0.0")]
    fn add(self, rhs: Rhs) -> Self::Output;
}
 
macro_rules! add_impl {
    ($($t:ty)*) => ($(
        #[stable(feature = "rust1", since = "1.0.0")]
        impl Add for $t {
            type Output = $t;
 
            #[inline]
            #[rustc_inherit_overflow_checks]
            fn add(self, other: $t) -> $t { self + other }
        }
 
        forward_ref_binop! { impl Add, add for $t, $t }
    )*)
}
 
add_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f32 f64 }
Непонятно только почему в определении трейта у нас Rhs
C++ (Qt)
1
fn add(self, rhs: Rhs) -> Self::Output;
А реализации уже какой-то other вылез
C++ (Qt)
1
fn add(self, other: $t) -> $t { self + other }
1
║XLR8║
1105 / 852 / 256
Регистрация: 25.07.2009
Сообщений: 4,180
Записей в блоге: 5
07.09.2020, 17:44 17
sodda, так какую задачу вы решаете? Вам надо swap? Какую задачу выполняет код в main?
0
Заблокирован
07.09.2020, 17:55  [ТС] 18
outoftime, код делает swap. Примитивный пример.
В Rust есть swap, но если посмотреть внутрь, то эта функция меняет местами сырые указатели в unsafe блоке.
А если самому попытаться реализовать swap привычным способом - через обмен с помощью третей переменной, то ничего не выйдет.
0
║XLR8║
1105 / 852 / 256
Регистрация: 25.07.2009
Сообщений: 4,180
Записей в блоге: 5
07.09.2020, 17:58 19
Цитата Сообщение от sodda Посмотреть сообщение
Непонятно только почему в определении трейта у нас Rhs
Разные люди писали?
0
Заблокирован
07.09.2020, 18:06  [ТС] 20
Цитата Сообщение от outoftime Посмотреть сообщение
Разные люди писали?
Сигнатура метода определена так, что вторым аргументом должен быть аргумент Rhs, а реализован метод так, что там уже other вместо Rhs.
Я макросы еще не изучал. Может это какой-то псевдоним определенный в самом макросе.
Rhs, к слову, расшифровывается как right-hand side. Этот акроним постоянно используются в подомного рода трейтах.
0
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
07.09.2020, 18:06

Заказываю контрольные, курсовые, дипломные работы и диссертации здесь.

Расширение функциональности pip
Сделал небольшой модуль для собственного удобства. Если кого-то заинтересует - ссылка внизу. Если...

режим ограниченной функциональности
Привет всем! После переустановки винды 7(64)все выглядит как то иначе. Полоса в низу рабочего...

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

Режим ограниченной функциональности
1) Найти сумму и число тех элементов заданного массива X1,X2, … ,Xn, которые попадают на заданный...

Расширение функциональности #define
Visual Studio 2012, Win7x64Prof У меня есть код условно на 10 строк, в котором меняется 2-3...

пробелмы с реализацией функциональности
Доброго времени суток! Я набросал интерфейс и добрался до заветной части наполнения его...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2021, vBulletin Solutions, Inc.