LINUX.ORG.RU

Сообщения DarkEld3r

 

Rust, ассоциировать функцию со структурой

Предположим, что я хочу сделать «контейнер», который хранит дженерик данные и в определённые моменты «валидировать» их:

struct Container<Data, Validate: Fn(&Data) -> bool> { ... }
Хранить функцию в каждом экземпляре структуры, по идее, нет необходимости, тем более, что в рантайме она меняться не будет. Следовательно хочется использовать PhantomData:
struct Container<Data, Validate: Fn(&Data) -> bool> {
    data: Vec<Data>,
    validate: PhantomData<Validate>,
}
Но дальше возникают вопросы.

Во первых, хочется иметь дефолтное значение, но написать struct Container<Validate: Fn(&Data) -> bool = foo> нельзя - ведь функция это значение, а не тип.

Во вторых, как это использовать? PhantomData только в качестве маркеров и пригодно?

В общем, можно ли как-то вообще извернуться или функцию всё-таки придётся хранить в объекте?

На плюсах (просьба не реагировать как на красную тряпку - просто для иллюстрации) это могло бы выглядеть как-то так:

bool foo(int) { return false; }

template <bool (*f)(int) = foo>
struct S {
    bool foo() { f(10); }
};

 

DarkEld3r
()

Раст, итераторы, командная строка

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

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

println!("{}", std::env::current_exe().unwrap().display());

Если обработать, то получается вот так:

println!("{}", std::env::current_exe().ok().map_or("test".to_string(), |path| path.display().to_string()));

Поэтому хотел не заморачивать и просто использовать первый аргумент. Получилось вот так:

let args = std::env::args();
if args.len() < 2 {
    println!("{}", args.take(1).last().unwrap_or("test".to_string()));
}

let text = args.skip(1).fold(String::new(), |result, param| result + " " + &param);

Собственно, вопросы:

1. Можно ли покрасивее получить имя программы?

2. Как поизящнее убрать первый пробел из получаемой строки?

3. Почему env::args возвращает итератор, а не вектор, как раньше?

 

DarkEld3r
()

Rust Alpha

Не следил активно за изменениями, но в своё время прочёл уже устаревшую Rust by Example и туториалы с официального сайта. Теперь вот начал неспешно читать новую «книгу», которая заменила собой старые гайды. Но некоторые моменты вызывают удивление. Знаю, что тут есть пару человек, которые в языке неплохо разбираются, так что может обьяснят почему сделали именно так.

1. Функции высшего порядка. Старый вариант:

fn test(f: |int| -> int) -> int
Новый:
fn test<F: Fn(i32) -> i32>(f: F) -> i32
Второй вариант, имхо, приближается к шаблонам С++ (в плохом смысле). Но ок, первая мысль - «подумали над уменьшением многословности для функций с одинаковыми сигнатурами». Не самый частый случай, но возможно, смысл есть. Потом читаю дальше:
fn test<F, G>(x: i32, f: F, g: G) -> i32
    where F: Fn(i32) -> i32, G: Fn(i32) -> i32 {
}
И объяснение:

That is because in Rust each closure has its own unique type. So, not only do closures with different signatures have different types, but different closures with the same signature have different types, as well!

По моему, они взяли худшее из обоих вариантов. Есть в этом какой-то смысл?

Опять же where тут, в отличии от хаскеля, выглядит коряво. Мы и так сначала заявляем, что вот у нас есть типы (<F, G>), потом ещё раз про них говорим (f: F, g: G) и наконец-то определяем их. Я бы ещё понял, если бы where позволяло отказаться от указания типов в угловых скобках.

2. Closures.

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

3. Named arguments, variable arguments.

В описании методов присутствует следующее:

Rust doesn't have method overloading, named arguments, or variable arguments. We employ the builder pattern instead.

Ну с перегрузкой ладно - аргументы за её отсутствие есть, да и некоторые языки без неё обходятся - жить можно. А вот с остальным? Раньше это всё обсуждалось, надеюсь не похоронили окончательно? Потому что страница кода с реализацией «билдера», приводимая как пример, совсем не вдохновляет.

tailgunner ozkriff numas13

 

DarkEld3r
()

Cargo, несколько таргетов

В прошлый раз на мои вопросы по расту успешно ответили, так что попробую ещё раз.

Хочу сделать следующее: два таргета использующих один модуль. Проще, наверное, будет кодом показать.

Структура проекта:

project
├── shared
│   └── mod.rs
├── target_1
│   └── main.rs
├── target_2
│   └── main.rs
Да, я в курсе, что рекомендуется иметь «src» директорию. Но честно говоря, не понимаю как она тут поможет. Да и не хочется идти по такому пути.

Cargo.toml

[[bin]]
name = "target_1"
path = "target_1/main.rs"

[[bin]]
name = "target_2"
path = "target_2/main.rs"

target_1/main.rs

#![feature(globs)]

mod shared;
use shared::*;

Получаю ошибку:

error: file not found for module `shared`

help: name the file either shared.rs or shared/mod.rs inside the directory target_1

Есть ли способ указать директорию «project» как корень для поиска модулей?

ozkriff tailgunner

 

DarkEld3r
()

Rust, несколько вопросов.

Возникло несколько вопросов, возможно, кто-то сможет подсказать.

1.

Either the trait or the type you're writing the impl for must be inside your crate.

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

2. Если у меня есть файл «test.rs», то содержимое окажется в модуле «test». Можно ли как-то изменить имя модуля не переименовывая файл?

tailgunner ozkriff theNamelessOne

DarkEld3r
()

RSS подписка на новые темы