LINUX.ORG.RU

И снова Rust

 ,


1

7

Я Вас, категорически, приветствую!

Вот, в всеми не любимой Delphi есть поля класса называемые property, при изменении которых можно использовать сеттеры и геттеры. В делфи коде, в одном из проектов, есть класс

TConfigManager = class
  ... 
  private
    function GetHost: String;
    procedure SetHost(AValue: String);
  public
    property host: String read GetHost write SetHost;
end;

При присваивание нового значения в host - это значение будет записано в ini-файл процедурой SetHost, при чтении будет прочитано из ini-файла с помощью функции GetHost. Притом код получается крайне локаничным:

  ConfigManager.Host := '127.0.0.1';
  MyHost := ConfigManager.Host;

Собственно, вопрос, как такое запилить в Rust?

★★★★

Была мысля заюзать перегрузку оператора =, но если в Delphi можно задать implicit и explicit, то в Rust атсасай =( Итого: Rust дважды сливает Delphi в данной задаче =( Возможно, макросы спасут, но пока не понял как использовать ty и tt

AntonyRF ★★★★
() автор топика

Была мысля заюзать перегрузку оператора =

...который не перегружается.

Итого: Rust дважды сливает Delphi в данной задаче

Ну используй Delphi, делов-то.

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

Итого: Rust дважды сливает Delphi в данной задаче

Это не задача, это метод решения задачи, ещё и неправильный.

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

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

anymacroname!(obj.prop = val)
Удобнее чем
obj.set_prop(val)

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

Это не задача, это метод решения задачи, ещё и неправильный.

Ну кстати да, метод решения. А чем не правильный?

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

который не перегружается.

как бы я про это и написал

, но если в Delphi можно задать implicit и explicit, то в Rust атсасай =(

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

Ну кстати да, метод решения. А чем не правильный?

Тем, что скрывает от программиста, что происходит не только перенос данных, но и какие-то действия под капотом.

А если чтение или запись файла случится с ошибкой, то что?

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

Слишком просто =)

Вот тебе вариант:

file.save(|saving_data| { saving_data.d = 0; saving_data } );

let mut d = ...;
file.load(|loaded_data| {d = loaded_data.d;});
O02eg ★★★★★
()
Последнее исправление: O02eg (всего исправлений: 1)
Ответ на: комментарий от O02eg

А если чтение или запись файла случится с ошибкой, то что?

Запись в файл в setter?! В голову приходит только класс конфига. QSettings подобным образом работает. Но в этом случае программист и так знает, что делает. И да, Qt ошибки пропускает =)

Тема - не холивар на тему нужные ли сеттеры/геттеры.

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

Насколько я знаю, такое сделать нельзя.

Нужно использовать подход как в C++: для простых присвоений делаем поле публичным, для сложных - приватный с сеттерами/геттерами.

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

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

При таком подходе действительно не место всякой подкапотной магии типа property, динамического создания методов/полей и т.д.

Crocodoom ★★★★★
()

Кстати. Свойства в плюсах (а значит и в расте, наверное) всё же можно сделать. Ну не свойства, а что-то очень близкое. Могу позже написать, как.

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

Напишите плиз, с макросами не додумался как сделать, а плагин к компилятору писать КМК изврат =)

AntonyRF ★★★★
() автор топика

в всеми не любимой Delphi

Что значит не любимой? Не любить можно php. А Delphi - он же мёртв. Используется только в самом глубоком интерпрайзе, в подвалах заводов.

th3m3 ★★★★★
()

На Scala такое делается в виде

class TConfigManager
{
     var host: String = ""
}

Или еще проще:

class TConfigManager
{
     var host = ""
}
Тип переменной host выводится компилятором. А если тебе host нужна как параметр конструктора класса, то вообще вот так: Или еще проще:
class TConfigManager(var host: String)
{
     ///
}

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

В Расте - нельзя, там operator= это всегда побитовое копирование

Ну вообще-то по умолчанию = это передача владения и если реализован типаж copy, то копирование

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

Move (передача владения) - это тоже побитовое копирование (если оптимизатор его не уберёт). Отличается от Copy только тем, что исходное значение больше не может использоваться.

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

Свойства в C++

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

#include <cstdio>

class int_ref
{
    private:
        int &ref;
    public:
        int_ref(int &x): ref(x) {}
        operator int()
        {
            printf("'Getter' has just been called!\n");
            //Other getter code...
            return ref;
        }
        int_ref operator =(const int &x)
        {
            printf("'Setter' has just been called!\n");
            //Other setter code...
            ref = x; return *this;
        }
};

class A
{
    private:
        int _x;
    public:
        int_ref x;
        A(int x_): _x(x_), x(_x) {}
};

int main()
{
    A a(3);

    a.x = 4; //'Setter' has just been called!
    int b = a.x; //'Getter' has just been called!

    printf("%d\n", b);
    return 0;
}

Вывод:

$ ./a.out 
'Setter' has just been called!
'Getter' has just been called!
4

Crocodoom ★★★★★
()

Используй crystal. Там сделать гетеры и сетеры так же легко как и в c# или как в твоём делфи.

class Obj
  def initialize
    @prop = 0
  end
  
  def prop
    puts "getter"
    @prop
  end
  
  def prop=(_prop)
    puts "setter. new value is #{_prop}"
    @prop = _prop
  end
end

obj = Obj.new
obj.prop = 5
puts obj.prop

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

Тут вон недавно был «ответ бузинеса недоучкам-обезьяноразработчикам» от true-компании разработчика очень важного и нужного софта, а именно CRM. Российской CRM, которая пилится индивидуально под чёрную бухгалтерию каждого заказчика. Судя по статье, они настолько круты, что круче их яйца в соотв. состоянии готовности.

Так вот, они пишут на Delphi!

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

В абсолютно технически, социально и научно отсталой стране использование Delphi - совершенно нормально. Мало того, нашим до создания ПО уровня Delphi - как пешком до Парижа, без помощи извне у нас бы до сих пор ваяли pascal'евский код в обычных текстовых редакторах с подсветкой синтаксиса.

Я понимаю предъявлять претензии к Delphi в США, но в России - разработчики должны быть благодарны за то, что добрые дяденьки с запада им это за нефтедоллары продают.

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

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

До этого бодался с Julia - но там в плане читабельности кода и скорости его написания всё плохо, если речь не идёт о вычислениях и выводе графики.

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

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

Видел. Честно говоря, я офигел) За последние лет 5, я никогда не встречал никаких вакансий с ним. Хоть я конечно не искал специально. Но всякое мелькало, а про Delphi - не слышно и не видно. А тут такая тру компания, и такой интерпайз во все поля. Уж лучше там была бы Java, на худой конец.

Вообще язык же неплохой

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

th3m3 ★★★★★
()

Я вспомнил, как делаются «свойства» на чистом Python с помощью внутренней классовой машинерии, упоролся и сообразил нечто. Неюзабельное, конечно, но выкидывать жалко: https://play.rust-lang.org/?gist=cd7b7d687bee6752d24fe2436c99b36e&version...

#![feature(unboxed_closures)]
#![feature(fn_traits)]

type SomeType = i32;

mod property {
    use SomeType;

    pub struct Property(pub SomeType);

    impl FnMut<(SomeType,)> for Property {
        extern "rust-call" fn call_mut(&mut self, args: (SomeType,)) {
            self.0 = args.0;
        }
    }

    impl FnOnce<(SomeType,)> for Property {
        type Output = ();
        extern "rust-call" fn call_once(self, _args: (SomeType,)) {}
    }
}

struct Foo {
    field: property::Property,
}

impl Foo {
    fn new(value: SomeType) -> Self {
        Self {
            field: property::Property(value),
        }
    }
}

fn main() {
    let mut foo = Foo::new(1);
    println!("{}", foo.field.0);
    (foo.field)(2);
    println!("{}", foo.field.0);
}
Virtuos86 ★★★★★
()
Ответ на: комментарий от anonymous

Перечисли мне все проекты написанные на С++, умник плиять

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

Тем, что скрывает от программиста, что происходит не только перенос данных, но и какие-то действия под капотом.

Интересно, что же ты скажешь, услышав про deep copy и конструкторы по умолчанию в C++...

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

Судя по статье, они настолько круты, что круче их яйца в соотв. состоянии готовности.
Так вот, они пишут на Delphi!

А уж какой крутой у них сайт(на самом деле нет), с раздражающей анимацией...

NextGenenration ★★
()

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

#![feature(proc_macro)]
#![allow(unused_assignments)]

extern crate proc_macro;

use proc_macro::TokenStream;

#[proc_macro]
pub fn property(input: TokenStream) -> TokenStream {
    // если макрос вызывается без ввода, то паникуем
    if input.is_empty() { panic!("Empty input!"); }

    let source = input.to_string();

    // если макрос вызывается с вводом, не являющимся путём ("path") к полю типа
    // то возвращаем ввод
    if let None = source.find('.') { return input; }

    let mut res = String::from("");
    if let Some(_) = source.find('=') {// ветка сеттера
        let split: Vec<&str> = source.split('.').map(|s| s.trim()).collect();
        let (strct, tail) = (split[0], split[1]);
        let split: Vec<&str> = tail.split('=').map(|s| s.trim()).collect();
        let (field, value) = (split[0], split[1]);
        res = String::from(strct) + "." + "set_" + field + "(" + value + ")";
     } else {// ветка геттера
        let split: Vec<&str> = source.split('.').map(|s| s.trim()).collect();
        let (strct, field) = (split[0], split[1]);
        res = String::from(strct) + "." + "get_" + field + "()";
    }
    res.parse().unwrap()
}
Содержимое Cargo.toml:
[package]
name = "property"
version = "0.1.0"
authors = ["Virtuos86 <virtuos86@xxx.xxx>"]

[dependencies]

[lib]
name = "property"
path = "lib.rs"
proc-macro = true

Пример использования:
#![feature(use_extern_macros)]

extern crate property;
use property::property as prop;

mod foo {
    type SomeType = i32;

    pub struct Foo {
        bar: SomeType // поле `field` приватно и недоступно из других модулей
    }

    impl Foo {
        pub fn new(value: SomeType) -> Self {
            Self { bar: value }
        }
        pub fn get_bar(&self) -> SomeType {
            self.bar
        }
        pub fn set_bar(&mut self, value: SomeType) {
            self.bar = value;
        }
    }
}

fn main() {
    use foo::Foo;
    let mut f = Foo::new(1);
    println!("{}", prop!(f.bar)); // пример использования геттера
    prop!(f.bar = 2); // пример использования сеттера
    println!("{}", prop!(f.bar));
}
Содержимое Cargo.toml примера (поскольку крэйт с макросом находится локально, а не на crates.io, нужно указать путь к нему):

[package]
name = "example_property"
version = "0.1.0"
authors = ["Virtuos86 <virtuos86@xxx.xxx>"]

[dependencies]
property = { path = "/root/rust/property/" }

Проверки в макросе на валидность ввода так себе, конечно, скорее для реалистичности.

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

prop это квалифицированный импорт для property::property, объявленный выше: use property::property as prop. Кому нужно, могут использовать полную форму.
prop!(...) — вызов макроса, а всё, что внутри это обычный доступ к полю объекта и присвоения значения полю объекта. Какие затруднения у тебя вызвал этот синтаксис?

Virtuos86 ★★★★★
()
Ответ на: комментарий от bread
if let None =
if let Some(_) =

Не распарсил. Што эта?

Как будто всё остальное ты распарсил. Ладно, предположим, что это так. if let это одна из форм сопоставления с образцом (pattern matching), используемых в синтаксисе Rust, аналогичная используемой в let-биндингах. Метод find типа String возвращает элемент перечисления (enum)Option<&str>, грубо говоря, или Есть(строка), или НичегоНет. Сопоставляя левую и правую части let-привязки, можно производить удобную распаковку значения, без unwrap, как и делается во втором случае.

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

Какие затруднения у тебя вызвал этот синтаксис?

«Избранные теги: common lisp, go, haskell, lisp, lua, ocaml, perl, pypy, python, python3, ruby, rust, scala, tcl, кодица, функциональное программирование, яр»

Теперь ясно. Развлекайся.

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

Прикольно, чо. Остальная моча кстати вполне понятна, правда приятней от этого не становится.

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

Я ленивый. Зачем следить за трекером в ожидании интересных тредов, если можно подписаться на теги? Так-то я кроме Питона и немного Раста ничего не знаю.

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

http://rurust.github.io/rust_book_ru/README.html — наверняка что-то устарело, но дружелюбно
https://doc.rust-lang.org/book/ — две редакции Книги, первая — оригинал той, что по первой ссылке, вторая — немного другая, по ощущениям больше заточена для практического использования языка, чем для пространных разговоров. Иногда удобнее искать инфу в одной, иногда в другой.
https://doc.rust-lang.org/std/ — здесь удобно искать подробную инфу по очередному трейту/типу/макросу, который в первый раз в глаза видишь.
Ну и онлайн-запускалка для тестирования и шаринга примеров кода: https://play.rust-lang.org/
Pretty formatting, проверка кода на вшивость (Clippy), просмотр генерируемого выхлопа LLVM, выбор сборок конпелятора (многие приятные фичи работают только в ночной сборке, но и стабильная тоже нужна) и даже немного настраиваемый редактор кода.

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

Да, критиковать удобнее, владея предметом обсуждения.

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

if let None =
if let Some(_) =

Не распарсил. Што эта?

То, что лучше бы записать вот так:

if source.find('.').is_none() { ... }
if source.find('=').is_some() { ... }

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

Да, то, что написал я, выглядит коряво. Единственное, что работает. По-хорошему, input надо не переводить в строку и парсить, а получать итератор по нему и матчить TokenNode. Примерный псевдокод:

for token_tree in input.into_iter() {
    let token_node = token_tree.kind;
    match token_node {
        ...
    }
}

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